UNPKG

mcp-use

Version:

Opinionated MCP Framework for TypeScript (@modelcontextprotocol/sdk compatible) - Build MCP Agents, Clients and Servers with support for ChatGPT Apps, Code Mode, OAuth, Notifications, Sampling, Observability and more.

2,609 lines 95.3 kB
import {
  applyProxyConfig,
  detectFavicon
} from "./chunk-4PQK35NP.js";
import {
  BrowserMCPClient
} from "./chunk-NSUUIFWS.js";
import {
  Tel,
  getPackageVersion
} from "./chunk-IBYLEFBS.js";
import {
  BrowserOAuthClientProvider,
  sanitizeUrl
} from "./chunk-M2YN2FFM.js";
import {
  __name
} from "./chunk-3GQAWCBQ.js";

// src/react/useMcp.ts
import { useCallback, useEffect, useMemo, useRef, useState } from "react";

// src/utils/assert.ts
function assert(condition, message) {
  if (!condition) {
    throw new Error(message);
  }
}
__name(assert, "assert");

// src/react/useMcp.ts
var DEFAULT_RECONNECT_DELAY = 3e3;
var DEFAULT_RETRY_DELAY = 5e3;
function useMcp(options) {
  const {
    url,
    enabled = true,
    callbackUrl = typeof window !== "undefined" ? sanitizeUrl(
      new URL("/oauth/callback", window.location.origin).toString()
    ) : "/oauth/callback",
    storageKeyPrefix = "mcp:auth",
    clientConfig = {},
    customHeaders = {},
    proxyConfig,
    debug: _debug = false,
    autoRetry = false,
    autoReconnect = DEFAULT_RECONNECT_DELAY,
    transportType = "auto",
    preventAutoAuth = false,
    // Default to false for backward compatibility (auto-trigger OAuth)
    useRedirectFlow = false,
    // Default to false for backward compatibility (use popup)
    onPopupWindow,
    timeout = 3e4,
    // 30 seconds default for connection timeout
    sseReadTimeout = 3e5,
    // 5 minutes default for SSE read timeout
    wrapTransport,
    onNotification,
    samplingCallback,
    onElicitation
  } = options;
  const { url: finalUrl, headers: proxyHeaders } = useMemo(
    () => applyProxyConfig(url || "", proxyConfig),
    [url, proxyConfig]
  );
  const allHeaders = useMemo(
    () => ({ ...proxyHeaders, ...customHeaders }),
    [proxyHeaders, customHeaders]
  );
  const [state, setState] = useState("discovering");
  const [tools, setTools] = useState([]);
  const [resources, setResources] = useState([]);
  const [resourceTemplates, setResourceTemplates] = useState([]);
  const [prompts, setPrompts] = useState([]);
  const [serverInfo, setServerInfo] = useState();
  const [capabilities, setCapabilities] = useState();
  const [error, setError] = useState(void 0);
  const [log, setLog] = useState([]);
  const [authUrl, setAuthUrl] = useState(void 0);
  const [authTokens, setAuthTokens] = useState(void 0);
  const clientRef = useRef(null);
  const authProviderRef = useRef(null);
  const iconLoadingPromiseRef = useRef(null);
  const connectingRef = useRef(false);
  const isMountedRef = useRef(true);
  const connectAttemptRef = useRef(0);
  const authTimeoutRef = useRef(null);
  const retryScheduledRef = useRef(false);
  const stateRef = useRef(state);
  const autoReconnectRef = useRef(autoReconnect);
  const successfulTransportRef = useRef(null);
  const connectRef = useRef(null);
  const failConnectionRef = useRef(null);
  useEffect(() => {
    stateRef.current = state;
    autoReconnectRef.current = autoReconnect;
  }, [state, autoReconnect]);
  const addLog = useCallback(
    (level, message, ...args) => {
      const fullMessage = args.length > 0 ? `${message} ${args.map((arg) => JSON.stringify(arg)).join(" ")}` : message;
      console[level](`[useMcp] ${fullMessage}`);
      if (isMountedRef.current) {
        setLog((prevLog) => [
          ...prevLog.slice(-100),
          { level, message: fullMessage, timestamp: Date.now() }
        ]);
      }
    },
    []
  );
  const disconnect = useCallback(
    async (quiet = false) => {
      if (!quiet) addLog("info", "Disconnecting...");
      connectingRef.current = false;
      if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
      authTimeoutRef.current = null;
      if (clientRef.current) {
        try {
          const serverName = "inspector-server";
          const session = clientRef.current.getSession(serverName);
          if (session && session._healthCheckCleanup) {
            session._healthCheckCleanup();
            session._healthCheckCleanup = null;
          }
          await clientRef.current.closeSession(serverName);
        } catch (err) {
          if (!quiet) addLog("warn", "Error closing session:", err);
        }
      }
      clientRef.current = null;
      if (isMountedRef.current && !quiet) {
        setState("discovering");
        setTools([]);
        setResources([]);
        setResourceTemplates([]);
        setPrompts([]);
        setError(void 0);
        setAuthUrl(void 0);
      }
    },
    [addLog]
  );
  const failConnection = useCallback(
    (errorMessage, connectionError) => {
      addLog("error", errorMessage, connectionError ?? "");
      if (isMountedRef.current) {
        setState("failed");
        setError(errorMessage);
        const manualUrl = authProviderRef.current?.getLastAttemptedAuthUrl();
        if (manualUrl) {
          setAuthUrl(manualUrl);
          addLog(
            "info",
            "Manual authentication URL may be available.",
            manualUrl
          );
        }
      }
      connectingRef.current = false;
      if (url) {
        Tel.getInstance().trackUseMcpConnection({
          url,
          transportType,
          success: false,
          errorType: connectionError?.name || "UnknownError",
          hasOAuth: !!authProviderRef.current,
          hasSampling: !!samplingCallback,
          hasElicitation: !!onElicitation
        }).catch(() => {
        });
      }
    },
    [addLog, url, transportType, samplingCallback, onElicitation]
  );
  const connect = useCallback(async () => {
    if (!enabled || !url) {
      addLog(
        "debug",
        enabled ? "No server URL provided, skipping connection." : "Connection disabled via enabled flag."
      );
      return;
    }
    if (connectingRef.current) {
      addLog("debug", "Connection attempt already in progress.");
      return;
    }
    if (!isMountedRef.current) {
      addLog("debug", "Connect called after unmount, aborting.");
      return;
    }
    connectingRef.current = true;
    connectAttemptRef.current += 1;
    setError(void 0);
    setAuthUrl(void 0);
    successfulTransportRef.current = null;
    setState("discovering");
    addLog(
      "info",
      `Connecting attempt #${connectAttemptRef.current} to ${url}...`
    );
    if (!authProviderRef.current) {
      authProviderRef.current = new BrowserOAuthClientProvider(url, {
        storageKeyPrefix,
        clientName: clientConfig.name,
        clientUri: clientConfig.uri,
        logoUri: clientConfig.logo_uri || "https://mcp-use.com/logo.png",
        callbackUrl,
        preventAutoAuth,
        useRedirectFlow,
        onPopupWindow
      });
      addLog("debug", "BrowserOAuthClientProvider initialized in connect.");
    }
    if (!clientRef.current) {
      clientRef.current = new BrowserMCPClient();
      addLog("debug", "BrowserMCPClient initialized in connect.");
    }
    const tryConnectWithTransport = /* @__PURE__ */ __name(async (transportTypeParam, isAuthRetry = false) => {
      addLog(
        "info",
        `Attempting connection with transport: ${transportTypeParam}`
      );
      try {
        const serverName = "inspector-server";
        const serverConfig = {
          url: finalUrl,
          transport: transportTypeParam === "sse" ? "http" : transportTypeParam,
          // Disable SSE fallback when using explicit HTTP transport (not SSE)
          // This prevents automatic HTTP → SSE fallback at the connector level
          disableSseFallback: transportTypeParam === "http",
          // Use SSE transport when explicitly requested
          preferSse: transportTypeParam === "sse",
          clientInfo: {
            name: "mcp-use Inspector",
            version: getPackageVersion()
          }
        };
        if (allHeaders && Object.keys(allHeaders).length > 0) {
          serverConfig.headers = allHeaders;
        }
        if (authProviderRef.current) {
          const tokens = await authProviderRef.current.tokens();
          if (tokens?.access_token) {
            serverConfig.headers = {
              ...serverConfig.headers,
              Authorization: `Bearer ${tokens.access_token}`
            };
          }
        }
        clientRef.current.addServer(serverName, {
          ...serverConfig,
          authProvider: authProviderRef.current,
          // ← SDK handles OAuth automatically!
          clientOptions: clientConfig,
          // ← Pass client config to connector
          samplingCallback,
          // ← Pass sampling callback to connector
          elicitationCallback: onElicitation,
          // ← Pass elicitation callback to connector
          wrapTransport: wrapTransport ? (transport) => {
            console.log(
              "[useMcp] Applying transport wrapper for server:",
              serverName,
              "url:",
              url
            );
            return wrapTransport(transport, url);
          } : void 0
        });
        const session = await clientRef.current.createSession(
          serverName,
          false
        );
        session.on("notification", (notification) => {
          onNotification?.(notification);
          if (notification.method === "notifications/tools/list_changed") {
            addLog("info", "Tools list changed, auto-refreshing...");
            refreshTools().catch(
              (err) => addLog("warn", "Auto-refresh tools failed:", err)
            );
          } else if (notification.method === "notifications/resources/list_changed") {
            addLog("info", "Resources list changed, auto-refreshing...");
            refreshResources().catch(
              (err) => addLog("warn", "Auto-refresh resources failed:", err)
            );
          } else if (notification.method === "notifications/prompts/list_changed") {
            addLog("info", "Prompts list changed, auto-refreshing...");
            refreshPrompts().catch(
              (err) => addLog("warn", "Auto-refresh prompts failed:", err)
            );
          }
        });
        await session.initialize();
        addLog("info", "\u2705 Successfully connected to MCP server");
        addLog("info", "Server info:", session.connector.serverInfo);
        addLog(
          "info",
          "Server capabilities:",
          session.connector.serverCapabilities
        );
        console.log("[useMcp] Server info:", session.connector.serverInfo);
        console.log(
          "[useMcp] Server capabilities:",
          session.connector.serverCapabilities
        );
        setState("ready");
        successfulTransportRef.current = transportTypeParam;
        const setupConnectionMonitoring = /* @__PURE__ */ __name(() => {
          let healthCheckInterval = null;
          let lastSuccessfulCheck = Date.now();
          const HEALTH_CHECK_INTERVAL = 1e4;
          const HEALTH_CHECK_TIMEOUT = 3e4;
          const checkConnectionHealth = /* @__PURE__ */ __name(async () => {
            if (!isMountedRef.current || stateRef.current !== "ready") {
              if (healthCheckInterval) {
                clearInterval(healthCheckInterval);
                healthCheckInterval = null;
              }
              return;
            }
            try {
              await session.connector.listTools();
              lastSuccessfulCheck = Date.now();
            } catch (err) {
              const timeSinceLastSuccess = Date.now() - lastSuccessfulCheck;
              if (timeSinceLastSuccess > HEALTH_CHECK_TIMEOUT) {
                addLog(
                  "warn",
                  `Connection appears to be broken (no response for ${Math.round(timeSinceLastSuccess / 1e3)}s), attempting to reconnect...`
                );
                if (healthCheckInterval) {
                  clearInterval(healthCheckInterval);
                  healthCheckInterval = null;
                }
                if (autoReconnectRef.current && isMountedRef.current) {
                  setState("discovering");
                  addLog("info", "Auto-reconnecting to MCP server...");
                  setTimeout(
                    () => {
                      if (isMountedRef.current && stateRef.current === "discovering") {
                        connect();
                      }
                    },
                    typeof autoReconnectRef.current === "number" ? autoReconnectRef.current : DEFAULT_RECONNECT_DELAY
                  );
                }
              }
            }
          }, "checkConnectionHealth");
          healthCheckInterval = setInterval(
            checkConnectionHealth,
            HEALTH_CHECK_INTERVAL
          );
          return () => {
            if (healthCheckInterval) {
              clearInterval(healthCheckInterval);
              healthCheckInterval = null;
            }
          };
        }, "setupConnectionMonitoring");
        if (autoReconnect) {
          const cleanup = setupConnectionMonitoring();
          session._healthCheckCleanup = cleanup;
        }
        Tel.getInstance().trackUseMcpConnection({
          url,
          transportType: transportTypeParam,
          success: true,
          hasOAuth: !!authProviderRef.current,
          hasSampling: !!samplingCallback,
          hasElicitation: !!onElicitation
        }).catch(() => {
        });
        setTools(session.connector.tools || []);
        const resourcesResult = await session.connector.listAllResources();
        setResources(resourcesResult.resources || []);
        const promptsResult = await session.connector.listPrompts();
        setPrompts(promptsResult.prompts || []);
        const serverInfo2 = session.connector.serverInfo;
        const capabilities2 = session.connector.serverCapabilities;
        if (serverInfo2) {
          console.log("[useMcp] Server info:", serverInfo2);
          setServerInfo(serverInfo2);
          const loadIconPromise = (async () => {
            try {
              const serverIcons = serverInfo2.icons;
              if (serverIcons && Array.isArray(serverIcons) && serverIcons.length > 0) {
                const iconUrl = serverIcons[0].src || serverIcons[0].url;
                if (iconUrl) {
                  addLog("info", "Server provided icon:", iconUrl);
                  const res = await fetch(iconUrl);
                  const blob = await res.blob();
                  const base64 = await new Promise(
                    (resolve, reject) => {
                      const reader = new FileReader();
                      reader.onloadend = () => resolve(reader.result);
                      reader.onerror = reject;
                      reader.readAsDataURL(blob);
                    }
                  );
                  if (isMountedRef.current) {
                    setServerInfo(
                      (prev) => prev ? { ...prev, icon: base64 } : void 0
                    );
                    addLog("debug", "Server icon converted to base64");
                  }
                  return base64;
                }
              }
              if (url) {
                const faviconBase64 = await detectFavicon(url);
                if (faviconBase64 && isMountedRef.current) {
                  setServerInfo(
                    (prev) => prev ? { ...prev, icon: faviconBase64 } : void 0
                  );
                  addLog("debug", "Favicon detected and added to serverInfo");
                  return faviconBase64;
                }
              }
              return null;
            } catch (err) {
              addLog("debug", "Icon loading failed (non-critical):", err);
              return null;
            }
          })();
          iconLoadingPromiseRef.current = loadIconPromise;
        }
        if (capabilities2) {
          console.log("[useMcp] Server capabilities:", capabilities2);
          setCapabilities(capabilities2);
        }
        if (authProviderRef.current) {
          const tokens = await authProviderRef.current.tokens();
          if (tokens?.access_token) {
            const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1e3 : void 0;
            setAuthTokens({
              access_token: tokens.access_token,
              token_type: tokens.token_type || "Bearer",
              expires_at: expiresAt,
              refresh_token: tokens.refresh_token,
              scope: tokens.scope
            });
          }
        }
        return "success";
      } catch (err) {
        const error2 = err;
        const errorMessage = error2?.message || String(err);
        if (error2.code === 401 || errorMessage.includes("401") || errorMessage.includes("Unauthorized")) {
          if (authProviderRef.current) {
            addLog(
              "info",
              "Authentication required. OAuth provider available."
            );
            try {
              const { auth } = await import("@modelcontextprotocol/sdk/client/auth.js");
              const baseUrl = new URL(url).origin;
              auth(authProviderRef.current, { serverUrl: baseUrl }).catch(
                () => {
                }
              );
              setTimeout(() => {
                if (isMountedRef.current) {
                  const manualUrl = authProviderRef.current?.getLastAttemptedAuthUrl();
                  if (manualUrl) {
                    setAuthUrl(manualUrl);
                    addLog(
                      "info",
                      "Manual authentication URL available:",
                      manualUrl
                    );
                  } else {
                    addLog("warn", "Could not generate authentication URL");
                  }
                }
              }, 100);
            } catch (authGenError) {
              addLog("warn", "Error generating auth URL:", authGenError);
            }
            if (isMountedRef.current) {
              setState("pending_auth");
            }
            connectingRef.current = false;
            return "auth_redirect";
          }
          if (customHeaders && Object.keys(customHeaders).length > 0) {
            failConnection(
              "Authentication failed: Server returned 401 Unauthorized. Check your Authorization header value is correct."
            );
            return "failed";
          }
          failConnection(
            "Authentication required: Server returned 401 Unauthorized. Add an Authorization header in the Custom Headers section (e.g., Authorization: Bearer YOUR_API_KEY)."
          );
          return "failed";
        }
        failConnection(
          errorMessage,
          error2 instanceof Error ? error2 : new Error(String(error2))
        );
        return "failed";
      }
    }, "tryConnectWithTransport");
    let finalStatus = "failed";
    if (transportType === "sse") {
      addLog("debug", "Using SSE-only transport mode");
      finalStatus = await tryConnectWithTransport("sse");
    } else if (transportType === "http") {
      addLog("debug", "Using HTTP-only transport mode");
      finalStatus = await tryConnectWithTransport("http");
    } else {
      addLog("debug", "Using auto transport mode (HTTP with SSE fallback)");
      const httpResult = await tryConnectWithTransport("http");
      if (httpResult === "fallback" && isMountedRef.current && stateRef.current !== "authenticating") {
        addLog("info", "HTTP failed, attempting SSE fallback...");
        const sseResult = await tryConnectWithTransport("sse");
        finalStatus = sseResult;
      } else {
        finalStatus = httpResult;
      }
    }
    if (finalStatus === "success" || finalStatus === "failed" || finalStatus === "auth_redirect") {
      connectingRef.current = false;
    }
    addLog("debug", `Connection sequence finished with status: ${finalStatus}`);
  }, [
    addLog,
    failConnection,
    disconnect,
    url,
    storageKeyPrefix,
    callbackUrl,
    clientConfig.name,
    clientConfig.version,
    clientConfig.uri,
    clientConfig.logo_uri,
    customHeaders,
    transportType,
    preventAutoAuth,
    useRedirectFlow,
    onPopupWindow,
    enabled,
    timeout,
    sseReadTimeout
  ]);
  useEffect(() => {
    connectRef.current = connect;
    failConnectionRef.current = failConnection;
  }, [connect, failConnection]);
  const callTool = useCallback(
    async (name, args, options2) => {
      if (stateRef.current !== "ready" || !clientRef.current) {
        throw new Error(
          `MCP client is not ready (current state: ${state}). Cannot call tool "${name}".`
        );
      }
      addLog("info", `Calling tool: ${name}`, args);
      const startTime = Date.now();
      try {
        const serverName = "inspector-server";
        const session = clientRef.current.getSession(serverName);
        if (!session) {
          throw new Error("No active session found");
        }
        const result = await session.connector.callTool(
          name,
          args || {},
          options2
        );
        addLog("info", `Tool "${name}" call successful:`, result);
        Tel.getInstance().trackUseMcpToolCall({
          toolName: name,
          success: true,
          executionTimeMs: Date.now() - startTime
        }).catch(() => {
        });
        return result;
      } catch (err) {
        addLog("error", `Tool "${name}" call failed:`, err);
        Tel.getInstance().trackUseMcpToolCall({
          toolName: name,
          success: false,
          errorType: err instanceof Error ? err.name : "UnknownError",
          executionTimeMs: Date.now() - startTime
        }).catch(() => {
        });
        throw err;
      }
    },
    [state]
  );
  const retry = useCallback(() => {
    if (stateRef.current === "failed") {
      addLog("info", "Retry requested...");
      connectRef.current?.();
    } else {
      addLog(
        "warn",
        `Retry called but state is not 'failed' (state: ${stateRef.current}). Ignoring.`
      );
    }
  }, [addLog]);
  const authenticate = useCallback(async () => {
    addLog("info", "Manual authentication requested...");
    const currentState = stateRef.current;
    if (currentState === "failed") {
      addLog("info", "Attempting to reconnect and authenticate via retry...");
      retry();
    } else if (currentState === "pending_auth") {
      addLog("info", "Proceeding with authentication from pending state...");
      try {
        assert(
          authProviderRef.current,
          "Auth Provider not available for manual auth"
        );
        assert(url, "Server URL is required for authentication");
        addLog("info", "Clearing all OAuth state and initiating fresh flow...");
        const hashPrefix = `${storageKeyPrefix}:${authProviderRef.current.serverUrlHash}`;
        Object.keys(localStorage).forEach((key) => {
          if (key.startsWith(hashPrefix)) {
            addLog("debug", `Removing stale OAuth key: ${key}`);
            localStorage.removeItem(key);
          }
          if (key.startsWith(`${storageKeyPrefix}:state_`)) {
            addLog("debug", `Removing orphaned state: ${key}`);
            localStorage.removeItem(key);
          }
        });
        setState("authenticating");
        const freshAuthProvider = new BrowserOAuthClientProvider(url, {
          storageKeyPrefix,
          clientName: clientConfig.name,
          clientUri: clientConfig.uri,
          logoUri: clientConfig.logo_uri || "https://mcp-use.com/logo.png",
          callbackUrl,
          preventAutoAuth: false,
          // ← Allow OAuth to proceed
          useRedirectFlow,
          onPopupWindow
        });
        authProviderRef.current = freshAuthProvider;
        addLog("info", "Triggering fresh OAuth authorization...");
        const { auth } = await import("@modelcontextprotocol/sdk/client/auth.js");
        const baseUrl = new URL(url).origin;
        auth(freshAuthProvider, {
          serverUrl: baseUrl
        }).catch((err) => {
          addLog(
            "info",
            "OAuth flow initiated:",
            err instanceof Error ? err.message : "Redirecting..."
          );
        });
      } catch (authError) {
        if (!isMountedRef.current) return;
        setState("pending_auth");
        addLog(
          "error",
          `Manual authentication failed: ${authError instanceof Error ? authError.message : String(authError)}`
        );
      }
    } else if (currentState === "authenticating") {
      addLog(
        "warn",
        "Already attempting authentication. Check for blocked popups or wait for timeout."
      );
      const manualUrl = authProviderRef.current?.getLastAttemptedAuthUrl();
      if (manualUrl && !authUrl) {
        setAuthUrl(manualUrl);
        addLog("info", "Manual authentication URL retrieved:", manualUrl);
      }
    } else {
      addLog(
        "info",
        `Client not in a state requiring manual authentication trigger (state: ${currentState}). If needed, try disconnecting and reconnecting.`
      );
    }
  }, [
    addLog,
    retry,
    authUrl,
    url,
    useRedirectFlow,
    onPopupWindow,
    storageKeyPrefix,
    clientConfig.name,
    clientConfig.uri,
    clientConfig.logo_uri,
    callbackUrl
  ]);
  const clearStorage = useCallback(() => {
    if (authProviderRef.current) {
      const count = authProviderRef.current.clearStorage();
      addLog("info", `Cleared ${count} item(s) from localStorage for ${url}.`);
      setAuthUrl(void 0);
      disconnect();
    } else {
      addLog("warn", "Auth provider not initialized, cannot clear storage.");
    }
  }, [url, addLog, disconnect]);
  const listResources = useCallback(async () => {
    if (stateRef.current !== "ready" || !clientRef.current) {
      throw new Error(
        `MCP client is not ready (current state: ${state}). Cannot list resources.`
      );
    }
    addLog("info", "Listing resources");
    try {
      const serverName = "inspector-server";
      const session = clientRef.current.getSession(serverName);
      if (!session) {
        throw new Error("No active session found");
      }
      const resourcesResult = await session.connector.listAllResources();
      setResources(resourcesResult.resources || []);
      addLog("info", "Resources listed successfully");
    } catch (err) {
      addLog("error", "List resources failed:", err);
      throw err;
    }
  }, [state]);
  const readResource = useCallback(
    async (uri) => {
      if (stateRef.current !== "ready" || !clientRef.current) {
        throw new Error(
          `MCP client is not ready (current state: ${state}). Cannot read resource.`
        );
      }
      addLog("info", `Reading resource: ${uri}`);
      try {
        const serverName = "inspector-server";
        const session = clientRef.current.getSession(serverName);
        if (!session) {
          throw new Error("No active session found");
        }
        const result = await session.connector.readResource(uri);
        addLog("info", "Resource read successful:", result);
        Tel.getInstance().trackUseMcpResourceRead({
          resourceUri: uri,
          success: true
        }).catch(() => {
        });
        return result;
      } catch (err) {
        addLog("error", "Resource read failed:", err);
        Tel.getInstance().trackUseMcpResourceRead({
          resourceUri: uri,
          success: false,
          errorType: err instanceof Error ? err.name : "UnknownError"
        }).catch(() => {
        });
        throw err;
      }
    },
    [state]
  );
  const listPrompts = useCallback(async () => {
    if (stateRef.current !== "ready" || !clientRef.current) {
      throw new Error(
        `MCP client is not ready (current state: ${state}). Cannot list prompts.`
      );
    }
    addLog("info", "Listing prompts");
    try {
      const serverName = "inspector-server";
      const session = clientRef.current.getSession(serverName);
      if (!session) {
        throw new Error("No active session found");
      }
      const promptsResult = await session.connector.listPrompts();
      setPrompts(promptsResult.prompts || []);
      addLog("info", "Prompts listed successfully");
    } catch (err) {
      addLog("error", "List prompts failed:", err);
      throw err;
    }
  }, [state]);
  const refreshTools = useCallback(async () => {
    if (stateRef.current !== "ready" || !clientRef.current) {
      addLog("debug", "Cannot refresh tools - client not ready");
      return;
    }
    addLog("debug", "Refreshing tools list");
    try {
      const serverName = "inspector-server";
      const session = clientRef.current.getSession(serverName);
      if (!session) {
        addLog("warn", "No active session found for tools refresh");
        return;
      }
      const toolsResult = await session.connector.listTools();
      setTools(toolsResult || []);
      addLog("info", "Tools list refreshed successfully");
    } catch (err) {
      addLog("warn", "Failed to refresh tools:", err);
    }
  }, [addLog]);
  const refreshResources = useCallback(async () => {
    if (stateRef.current !== "ready" || !clientRef.current) {
      addLog("debug", "Cannot refresh resources - client not ready");
      return;
    }
    addLog("debug", "Refreshing resources list");
    try {
      const serverName = "inspector-server";
      const session = clientRef.current.getSession(serverName);
      if (!session) {
        addLog("warn", "No active session found for resources refresh");
        return;
      }
      const resourcesResult = await session.connector.listAllResources();
      setResources(resourcesResult.resources || []);
      addLog("info", "Resources list refreshed successfully");
    } catch (err) {
      addLog("warn", "Failed to refresh resources:", err);
    }
  }, [addLog]);
  const refreshPrompts = useCallback(async () => {
    if (stateRef.current !== "ready" || !clientRef.current) {
      addLog("debug", "Cannot refresh prompts - client not ready");
      return;
    }
    addLog("debug", "Refreshing prompts list");
    try {
      const serverName = "inspector-server";
      const session = clientRef.current.getSession(serverName);
      if (!session) {
        addLog("warn", "No active session found for prompts refresh");
        return;
      }
      const promptsResult = await session.connector.listPrompts();
      setPrompts(promptsResult.prompts || []);
      addLog("info", "Prompts list refreshed successfully");
    } catch (err) {
      addLog("warn", "Failed to refresh prompts:", err);
    }
  }, [addLog]);
  const refreshAll = useCallback(async () => {
    addLog("info", "Refreshing all lists (tools, resources, prompts)");
    await Promise.all([refreshTools(), refreshResources(), refreshPrompts()]);
  }, [refreshTools, refreshResources, refreshPrompts, addLog]);
  const getPrompt = useCallback(
    async (name, args) => {
      if (stateRef.current !== "ready" || !clientRef.current) {
        throw new Error(
          `MCP client is not ready (current state: ${state}). Cannot get prompt.`
        );
      }
      addLog("info", `Getting prompt: ${name}`, args);
      try {
        const serverName = "inspector-server";
        const session = clientRef.current.getSession(serverName);
        if (!session) {
          throw new Error("No active session found");
        }
        const result = await session.connector.getPrompt(name, args || {});
        addLog("info", `Prompt "${name}" retrieved successfully:`, result);
        return result;
      } catch (err) {
        addLog("error", `Prompt "${name}" retrieval failed:`, err);
        throw err;
      }
    },
    [state]
  );
  useEffect(() => {
    const messageHandler = /* @__PURE__ */ __name((event) => {
      if (event.origin !== window.location.origin) return;
      if (event.data?.type === "mcp_auth_callback") {
        addLog("info", "Received auth callback message.", event.data);
        if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
        authTimeoutRef.current = null;
        if (event.data.success) {
          addLog(
            "info",
            "Authentication successful via popup. Reconnecting client..."
          );
          if (connectingRef.current) {
            addLog(
              "debug",
              "Connection attempt already in progress, resetting flag to allow reconnection."
            );
          }
          connectingRef.current = false;
          setTimeout(() => {
            if (isMountedRef.current) {
              addLog(
                "debug",
                "Initiating reconnection after successful auth callback."
              );
              connectRef.current?.();
            }
          }, 100);
        } else {
          failConnectionRef.current?.(
            `Authentication failed in callback: ${event.data.error || "Unknown reason."}`
          );
        }
      }
    }, "messageHandler");
    window.addEventListener("message", messageHandler);
    addLog("debug", "Auth callback message listener added.");
    return () => {
      window.removeEventListener("message", messageHandler);
      addLog("debug", "Auth callback message listener removed.");
      if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
    };
  }, [addLog]);
  useEffect(() => {
    isMountedRef.current = true;
    if (!enabled || !url) {
      addLog(
        "debug",
        enabled ? "No server URL provided, skipping connection." : "Connection disabled via enabled flag."
      );
      setState("discovering");
      return () => {
        isMountedRef.current = false;
      };
    }
    addLog("debug", "useMcp mounted, initiating connection.");
    connectAttemptRef.current = 0;
    if (!authProviderRef.current || authProviderRef.current.serverUrl !== url) {
      authProviderRef.current = new BrowserOAuthClientProvider(url, {
        storageKeyPrefix,
        clientName: clientConfig.name,
        clientUri: clientConfig.uri,
        logoUri: clientConfig.logo_uri || "https://mcp-use.com/logo.png",
        callbackUrl,
        preventAutoAuth,
        useRedirectFlow,
        onPopupWindow
      });
      addLog(
        "debug",
        "BrowserOAuthClientProvider initialized/updated on mount/option change."
      );
    }
    connect();
    return () => {
      isMountedRef.current = false;
      addLog("debug", "useMcp unmounting, disconnecting.");
      disconnect(true);
    };
  }, [
    url,
    enabled,
    storageKeyPrefix,
    callbackUrl,
    clientConfig.name,
    clientConfig.version,
    clientConfig.uri,
    clientConfig.logo_uri,
    useRedirectFlow
  ]);
  const retryRef = useRef(retry);
  const addLogRef = useRef(addLog);
  useEffect(() => {
    retryRef.current = retry;
    addLogRef.current = addLog;
  }, [retry, addLog]);
  useEffect(() => {
    let retryTimeoutId = null;
    if (state === "failed" && autoRetry && connectAttemptRef.current > 0) {
      if (!retryScheduledRef.current) {
        retryScheduledRef.current = true;
        const delay = typeof autoRetry === "number" ? autoRetry : DEFAULT_RETRY_DELAY;
        addLogRef.current(
          "info",
          `Connection failed, auto-retrying in ${delay}ms...`
        );
        retryTimeoutId = setTimeout(() => {
          retryScheduledRef.current = false;
          if (isMountedRef.current && stateRef.current === "failed") {
            retryRef.current();
          }
        }, delay);
      }
    } else if (state !== "failed") {
      retryScheduledRef.current = false;
    }
    return () => {
      if (retryTimeoutId) {
        clearTimeout(retryTimeoutId);
        retryScheduledRef.current = false;
      }
    };
  }, [state, autoRetry]);
  const ensureIconLoaded = useCallback(async () => {
    if (stateRef.current !== "ready") {
      addLog("warn", "Cannot ensure icon loaded - not connected");
      return null;
    }
    if (serverInfo?.icon) {
      return serverInfo.icon;
    }
    if (iconLoadingPromiseRef.current) {
      addLog("debug", "Waiting for icon to finish loading...");
      const icon = await iconLoadingPromiseRef.current;
      return icon;
    }
    addLog("debug", "No icon available and no loading in progress");
    return null;
  }, [serverInfo, addLog]);
  return {
    state,
    name: serverInfo?.name || url || "",
    tools,
    resources,
    resourceTemplates,
    prompts,
    serverInfo,
    capabilities,
    error,
    log,
    authUrl,
    authTokens,
    client: clientRef.current,
    callTool,
    readResource,
    listResources,
    listPrompts,
    getPrompt,
    refreshTools,
    refreshResources,
    refreshPrompts,
    refreshAll,
    retry,
    disconnect,
    authenticate,
    clearStorage,
    ensureIconLoaded
  };
}
__name(useMcp, "useMcp");

// src/react/ErrorBoundary.tsx
import React from "react";
var ErrorBoundary = class extends React.Component {
  static {
    __name(this, "ErrorBoundary");
  }
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }
  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }
  componentDidCatch(error, errorInfo) {
    console.error("Widget Error:", error, errorInfo);
  }
  render() {
    if (this.state.hasError) {
      return /* @__PURE__ */ React.createElement("div", { className: "p-4 border border-red-500 bg-red-50 text-red-900 rounded-md dark:bg-red-900/20 dark:text-red-100" }, /* @__PURE__ */ React.createElement("h3", { className: "font-bold mb-2" }, "Widget Error"), /* @__PURE__ */ React.createElement("pre", { className: "text-sm whitespace-pre-wrap" }, this.state.error?.message));
    }
    return this.props.children;
  }
};

// src/react/Image.tsx
import React2 from "react";
var Image = /* @__PURE__ */ __name(({ src, ...props }) => {
  const publicUrl = typeof window !== "undefined" ? window.__mcpPublicAssetsUrl || window.__mcpPublicUrl || "" : "";
  const getFinalSrc = /* @__PURE__ */ __name((source) => {
    if (!source) return source;
    if (source.startsWith("http://") || source.startsWith("https://") || source.startsWith("data:")) {
      return source;
    }
    if (!publicUrl) {
      return source;
    }
    const cleanSrc = source.startsWith("/") ? source.slice(1) : source;
    return `${publicUrl}/${cleanSrc}`;
  }, "getFinalSrc");
  const finalSrc = getFinalSrc(src);
  return /* @__PURE__ */ React2.createElement("img", { src: finalSrc, ...props });
}, "Image");

// src/react/ThemeProvider.tsx
import React3, { useEffect as useEffect3, useLayoutEffect, useState as useState3 } from "react";

// src/react/useWidget.ts
import {
  useCallback as useCallback2,
  useEffect as useEffect2,
  useMemo as useMemo2,
  useState as useState2,
  useSyncExternalStore
} from "react";

// src/react/widget-types.ts
var SET_GLOBALS_EVENT_TYPE = "openai:set_globals";

// src/react/useWidget.ts
function useOpenAiGlobal(key) {
  return useSyncExternalStore(
    (onChange) => {
      const handleSetGlobal = /* @__PURE__ */ __name((event) => {
        const customEvent = event;
        const value = customEvent.detail.globals[key];
        if (value === void 0) {
          return;
        }
        onChange();
      }, "handleSetGlobal");
      if (typeof window !== "undefined") {
        window.addEventListener(SET_GLOBALS_EVENT_TYPE, handleSetGlobal);
      }
      return () => {
        if (typeof window !== "undefined") {
          window.removeEventListener(SET_GLOBALS_EVENT_TYPE, handleSetGlobal);
        }
      };
    },
    () => typeof window !== "undefined" && window.openai ? window.openai[key] : void 0
  );
}
__name(useOpenAiGlobal, "useOpenAiGlobal");
function useWidget(defaultProps) {
  const [isOpenAiAvailable, setIsOpenAiAvailable] = useState2(
    () => typeof window !== "undefined" && !!window.openai
  );
  useEffect2(() => {
    if (typeof window !== "undefined" && window.openai) {
      setIsOpenAiAvailable(true);
      return;
    }
    const checkInterval = setInterval(() => {
      if (typeof window !== "undefined" && window.openai) {
        setIsOpenAiAvailable(true);
        clearInterval(checkInterval);
      }
    }, 100);
    const handleSetGlobals = /* @__PURE__ */ __name(() => {
      if (typeof window !== "undefined" && window.openai) {
        setIsOpenAiAvailable(true);
        clearInterval(checkInterval);
      }
    }, "handleSetGlobals");
    if (typeof window !== "undefined") {
      window.addEventListener(SET_GLOBALS_EVENT_TYPE, handleSetGlobals);
    }
    const timeout = setTimeout(() => {
      clearInterval(checkInterval);
      if (typeof window !== "undefined") {
        window.removeEventListener(SET_GLOBALS_EVENT_TYPE, handleSetGlobals);
      }
    }, 5e3);
    return () => {
      clearInterval(checkInterval);
      clearTimeout(timeout);
      if (typeof window !== "undefined") {
        window.removeEventListener(SET_GLOBALS_EVENT_TYPE, handleSetGlobals);
      }
    };
  }, []);
  const provider = useMemo2(() => {
    return isOpenAiAvailable ? "openai" : "mcp-ui";
  }, [isOpenAiAvailable]);
  const searchString = typeof window !== "undefined" ? window.location.search : "";
  const urlParams = useMemo2(() => {
    const urlParams2 = new URLSearchParams(searchString);
    if (urlParams2.has("mcpUseParams")) {
      return JSON.parse(urlParams2.get("mcpUseParams"));
    }
    return {
      toolInput: {},
      toolOutput: {},
      toolId: ""
    };
  }, [searchString]);
  const toolInput = provider === "openai" ? useOpenAiGlobal("toolInput") : urlParams.toolInput;
  const toolOutput = provider === "openai" ? useOpenAiGlobal("toolOutput") : urlParams.toolOutput;
  const toolResponseMetadata = useOpenAiGlobal("toolResponseMetadata");
  const widgetProps = useMemo2(() => {
    if (toolResponseMetadata && typeof toolResponseMetadata === "object") {
      const metaProps = toolResponseMetadata["mcp-use/props"];
      if (metaProps) {
        return metaProps;
      }
    }
    return defaultProps || {};
  }, [toolResponseMetadata, defaultProps]);
  const widgetState = useOpenAiGlobal("widgetState");
  const theme = useOpenAiGlobal("theme");
  const displayMode = useOpenAiGlobal("displayMode");
  const safeArea = useOpenAiGlobal("safeArea");
  const maxHeight = useOpenAiGlobal("maxHeight");
  const userAgent = useOpenAiGlobal("userAgent");
  const locale = useOpenAiGlobal("locale");
  const mcp_url = useMemo2(() => {
    if (typeof window !== "undefined" && window.__mcpPublicUrl) {
      return window.__mcpPublicUrl.replace(/\/mcp-use\/public$/, "");
    }
    return "";
  }, []);
  const [localWidgetState, setLocalWidgetState] = useState2(null);
  useEffect2(() => {
    if (widgetState !== void 0) {
      setLocalWidgetState(widgetState);
    }
  }, [widgetState]);
  const callTool = useCallback2(
    async (name, args) => {
      if (!window.openai?.callTool) {
        throw new Error("window.openai.callTool is not available");
      }
      return window.openai.callTool(name, args);
    },
    []
  );
  const sendFollowUpMessage = useCallback2(
    async (prompt) => {
      if (!window.openai?.sendFollowUpMessage) {
        throw new Error("window.openai.sendFollowUpMessage is not available");
      }
      return window.openai.sendFollowUpMessage({ prompt });
    },
    []
  );
  const openExternal = useCallback2((href) => {
    if (!window.openai?.openExternal) {
      throw new Error("window.openai.openExternal is not available");
    }
    window.openai.openExternal({ href });
  }, []);
  const requestDisplayMode = useCallback2(
    async (mode) => {
      if (!window.openai?.requestDisplayMode) {
        throw new Error("window.openai.requestDisplayMode is not available");
      }
      return window.openai.requestDisplayMode({ mode });
    },
    []
  );
  const setState = useCallback2(
    async (state) => {
      if (!window.openai?.setWidgetState) {
        throw new Error("window.openai.setWidgetState is not available");
      }
      const currentState = widgetState !== void 0 ? widgetState : localWidgetState;
      const newState = typeof state === "function" ? state(currentState) : state;
      setLocalWidgetState(newState);
      return window.openai.setWidgetState(newState);
    },
    [widgetState, localWidgetState]
  );
  const isPending = useMemo2(() => {
    return provider === "openai" && toolResponseMetadata === null;
  }, [provider, toolResponseMetadata]);
  return {
    // Props and state (with defaults)
    props: widgetProps,
    toolInput: toolInput || {},
    output: toolOutput ?? null,
    metadata: toolResponseMetadata ?? null,
    state: localWidgetState,
    setState,
    // Layout and theme (with safe defaults)
    theme: theme || "light",
    displayMode: displayMode || "inline",
    safeArea: safeArea || { insets: { top: 0, bottom: 0, left: 0, right: 0 } },
    maxHeight: maxHeight || 600,
    userAgent: userAgent || {
      device: { type: "desktop" },
      capabilities: { hover: true, touch: false }
    },
    locale: locale || "en",
    mcp_url,
    // Actions
    callTool,
    sendFollowUpMessage,
    openExternal,
    requestDisplayMode,
    // Availability
    isAvailable: isOpenAiAvailable,
    isPending
  };
}
__name(useWidget, "useWidget");
function useWidgetProps(defaultProps) {
  const { props } = useWidget(defaultProps);
  return props;
}
__name(useWidgetProps, "useWidgetProps");
function useWidgetTheme() {
  const { theme } = useWidget();
  return theme;
}
__name(useWidgetTheme, "useWidgetTheme");
function useWidgetState(defaultState) {
  const { state, setState } = useWidget();
  useEffect2(() => {
    if (state === null && defaultState !== void 0 && window.openai?.setWidgetState) {
      setState(defaultState);
    }
  }, []);
  return [state, setState];
}
__name(useWidgetState, "useWidgetState");

// src/react/ThemeProvider.tsx
var ThemeProvider = /* @__PURE__ */ __name(({
  children
}) => {
  const { theme, isAvailable } = useWidget();
  console.log("theme", theme);
  const [systemPreference, setSystemPreference] = useState3(
    () => {
      if (typeof window === "undefined") return "light";
      return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
    }
  );
  useEffect3(() => {
    if (typeof window === "undefined") return;
    const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
    const handleChange = /* @__PURE__ */ __name((e) => {
      setSystemPreference(e.matches ? "dark" : "light");
    }, "handleChange");
    mediaQuery.addEventListener("change", handleChange);
    return () => mediaQuery.removeEventListener("change", handleChange);
  }, []);
  const effectiveTheme = isAvailable ? theme : systemPreference;
  useLayoutEffect(() => {
    if (typeof document === "undefined") return;
    if (effectiveTheme === "dark") {
      document.documentElement.classList.add("dark");
    } else {
      document.documentElement.classList.remove("dark");
    }
  }, [effectiveTheme]);
  return /* @__PURE__ */ React3.createElement(React3.Fragment, null, children);
}, "ThemeProvider");

// src/react/WidgetControls.tsx
import React4, { useEffect as useEffect4, useRef as useRef2, useState as useState4 } from "react";
function WidgetControls({
  children,
  className = "",
  position = "top-right",
  attachTo,
  showLabels = true,
  debugger: enableDebugger = false,
  viewControls = false
}) {
  const {
    props,
    output,
    metadata,
    state,
    theme,
    displayMode,
    safeArea,
    maxHeight,
    userAgent,
    locale,
    isAvailable,
    callTool,
    sendFollowUpMessage,
    openExternal,
    requestDisplayMode,
    setState
  } = useWidget();
  const [isHovered, setIsHovered] = useState4(false);
  const [isOverlayOpen, setIsOverlayOpen] = useState4(false);
  const containerRef = useRef2(null);
  const overlayRef = useRef2(null);
  const [windowOpenAiKeys, setWindowOpenAiKeys] = useState4([]);
  const [actionResult, setActionResult] = useState4("");
  const [toolName, setToolName] = useState4("get-my-city");
  const [toolArgs, setToolArgs] = useState4("{}");
  const [followUpMessage, setFollowUpMessage] = useState4(
    "Test follow-up message"
  );
  const [externalUrl, setExternalUrl] = useState4(
    "https://docs.mcp-use.com"
  );
  const isFullscreen = displayMode === "fullscreen" && isAvailable;
  const isPip = displayMode === "pip" && isAvailable;
  const isInInspector = typeof window !== "undefined" && window.location.pathname.includes("/inspector/api/");
  useEffect4(() => {
    const timeoutId = setTimeout(() => {
      if (typeof window !== "undefined" && window.openai) {
        try {
          const keys = Object.keys(window.openai);
          setWindowOpenAiKeys(keys);
        } catch (e) {
          setWindowOpenAiKeys([]);
        }
      } else {
        setWindowOpenAiKeys([]);
      }
    }, 100);
    return () => {
      clearTimeout(timeoutId);
    };
  }, []);
  const isDark = theme === "dark";
  const getPositionClasses = /* @__PURE__ */ __name(() => {
    const baseClasses = [
      "absolute",
      "z-[1000]",
      "flex",
      "gap-2",
      "transition-opacity",
      "duration-200",
      "ease-in-out",
      isHovered ? "opacity-100" : "opacity-0",
      isHovered ? "pointer-events-auto" : "pointer-events-none"
    ];
    switch (position) {
      case "top-left":
        return [...baseClasses, "top-4", "left-4"];
      case "top-center":
        return [...baseClasses, "top-4", "left-1/2", "-translate-x-1/2"];
      case "top-right":
        return [...baseClasses, "top-4", "right-4"];
      case "center-left":
        return [...baseClasses, "top-1/2", "left-4", "-translate-y-1/2"];
      case "center-right":
        return [...baseClasses, "top-1/2", "right-4", "-translate-y-1/2"];
      case "bottom-left":
        return [...baseClasses, "bottom-4", "left-4"];
      case "bottom-center":
        return [...baseClasses, "bottom-4", "left-1/2", "-translate-x-1/2"];
      case "bottom-right":
        return [...baseClasses, "bottom-4", "right-4"];
      default:
        return [...baseClasses, "top-4", "right-4"];
    }
  }, "getPositionClasses");
  const getPositionOffsetStyles = /* @__PURE__ */ __name(() => {
    const baseOffset = 16;
    const topOffset = safeArea?.insets?.top ? Math.max(baseOffset, safeArea.insets.top + 8) : baseOffset;
    const rightOffset = safeArea?.insets?.right ? Math.max(baseOffset, safeArea.insets.right + 8) : baseOffset;
    const bottomOffset = safeArea?.insets?.bottom ? Math.max(baseOffset, safeArea.insets.bottom + 8) : baseOffset;
    const leftOffset = safeArea?.insets?.left ? Math.max(baseOffset, safeArea.insets.left + 8) : baseOffset;
    const styles = {};
    switch (position) {
      case "top-left":
        styles.top = `${topOffset}px`;
        styles.left = `${leftOffset}px`;
        break;
      case "top-center":
        styles.top = `${topOffset}px`;
        break;
      case "top-right":
        styles.top = `${topOffset}px`;
        styles.right = `${rightOffset}px`;
        break;
      case "center-left":
        styles.left = `${leftOffset}px`;
        break;
      case "center-right":
        styles.right = `${rightOffset}px`;
        break;
      case "bottom-left":
        styles.bottom = `${bottomOffset}px`;
        styles.left = `${leftOffset}px`;
        break;
      case "bottom-center":
        styles.bottom = `${bottomOffset}px`;
        break;
      case "bottom-right":
        styles.bottom = `${bottomOffset}px`;
        styles.right = `${rightOffset}px`;
        break;
      default:
        styles.top = `${topOffset}px`;
        styles.right = `${rightOffset}px`;
        break;
    }
    return styles;
  }, "getPositionOffsetStyles");
  useEffect4(() => {
    if (!attachTo) return;
    const handleMouseEnter = /* @__PURE__ */ __name(() => setIsHovered(true), "handleMouseEnter");
    const handleMouseLeave = /* @__PURE__ */ __name(() => setIsHovered(false), "handleMouseLeave");
    attachTo.addEventListener("mouseenter", handleMouseEnter);
    attachTo.addEventListener("mouseleave", handleMouseLeave);
    return () => {
      attachTo.removeEventListener("mouseenter", handleMouseEnter);
      attachTo.removeEventListener("mouseleave", handleMouseLeave);
    };
  }, [attachTo]);
  useEffect4(() => {
    if (!isOverlayOpen) return;
    const handleClickOutside = /* @__PURE__ */ __name((event) => {
      if (overlayRef.current && !overlayRef.current.contains(event.target)) {
        setIsOverlayOpen(false);
      }
    }, "handleClickOutside");
    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [isOverlayOpen]);
  useEffect4(() => {
    if (isOverlayOpen) {
      document.body.style.overflow = "hidden";
    } else {
      document.body.style.overflow = "";
    }
    return () => {
      document.body.style.overflow = "";
    };
  }, [isOverlayOpen]);
  const handleToggleOverlay = /* @__PURE__ */ __name(() => {
    setIsOverlayOpen(!isOverlayOpen);
  }, "handleToggleOverlay");
  const handleCallTool = /* @__PURE__ */ __name(async () => {
    try {
      setActionResult("Calling tool...");
      const args = toolArgs.trim() ? JSON.parse(toolArgs) : {};
      const result = await callTool(toolName, args);
      setActionResult(`Success: ${JSON.stringify(result, null, 2)}`);
    } catch (error) {
      const err = error;
      setActionResult(`Error: ${err.message}`);
    }
  }, "handleCallTool");
  const handleSendFollowUpMessage = /* @__PURE__ */ __name(async () => {
    try {
      setActionResult("Sending follow-up message...");
      await sendFollowUpMessage(followUpMessage);
      setActionResult("Follow-up message sent successfully");
    } catch (error) {
      const err = error;
      setActionResult(`Error: ${err.message}`);
    }
  }, "handleSendFollowUpMessage");
  const handleOpenExternal = /* @__PURE__ */ __name(() => {
    try {
      openExternal(externalUrl);
      setActionResult(`Opened external link: ${externalUrl}`);
    } catch (error) {
      const err = error;
      setActionResult(`Error: ${err.message}`);
    }
  }, "handleOpenExternal");
  const handleRequestDisplayMode = /* @__PURE__ */ __name(async (mode) => {
    try {
      setActionResult(`Requesting display mode: ${mode}...`);
      const result = await requestDisplayMode(mode);
      setActionResult(`Display mode granted: ${result.mode}`);
    } catch (error) {
      const err = error;
      setActionResult(`Error: ${err.message}`);
    }
  }, "handleRequestDisplayMode");
  const handleSetState = /* @__PURE__ */ __name(async () => {
    try {
      const newState = state ? { ...state, debugTimestamp: (/* @__PURE__ */ new Date()).toISOString() } : { debugTimestamp: (/* @__PURE__ */ new Date()).toISOString() };
      setActionResult("Setting state...");
      await setState(newState);
      setActionResult(`State updated: ${JSON.stringify(newState, null, 2)}`);
    } catch (error) {
      const err = error;
      setActionResult(`Error: ${err.message}`);
    }
  }, "handleSetState");
  const handleFullscreen = /* @__PURE__ */ __name(async () => {
    try {
      await requestDisplayMode("fullscreen");
    } catch (error) {
      console.error("Failed to go fullscreen:", error);
    }
  }, "handleFullscreen");
  const handlePip = /* @__PURE__ */ __name(async () => {
    try {
      await requestDisplayMode("pip");
    } catch (error) {
      console.error("Failed to go pip:", error);
    }
  }, "handlePip");
  const getTooltipClasses = /* @__PURE__ */ __name(() => {
    const baseClasses = [
      "absolute",
      "px-2",
      "py-1",
      "bg-black/90",
      "text-white",
      "rounded",
      "text-xs",
      "whitespace-nowrap",
      "pointer-events-none",
      "transition-opacity",
      "duration-200",
      "ease-in-out"
    ];
    switch (position) {
      case "top-right":
        return [...baseClasses, "top-full", "right-0", "mt-2"];
      case "top-left":
        return [...baseClasses, "top-full", "left-0", "mt-2"];
      case "top-center":
        return [
          ...baseClasses,
          "top-full",
          "left-1/2",
          "-translate-x-1/2",
          "mt-2"
        ];
      case "bottom-right":
        return [...baseClasses, "bottom-full", "right-0", "mb-2"];
      case "bottom-left":
        return [...baseClasses, "bottom-full", "left-0", "mb-2"];
      case "bottom-center":
        return [
          ...baseClasses,
          "bottom-full",
          "left-1/2",
          "-translate-x-1/2",
          "mb-2"
        ];
      case "center-left":
        return [
          ...baseClasses,
          "left-full",
          "top-1/2",
          "-translate-y-1/2",
          "ml-2"
        ];
      case "center-right":
        return [
          ...baseClasses,
          "right-full",
          "top-1/2",
          "-translate-y-1/2",
          "mr-2"
        ];
      default:
        return [...baseClasses, "top-full", "right-0", "mt-2"];
    }
  }, "getTooltipClasses");
  const IconButton = /* @__PURE__ */ __name(({
    onClick,
    label,
    children: icon
  }) => {
    const [isButtonHovered, setIsButtonHovered] = useState4(false);
    const tooltipClasses = getTooltipClasses();
    return /* @__PURE__ */ React4.createElement(
      "button",
      {
        className: `p-2 ${isDark ? "bg-white/10 hover:bg-white/20" : "bg-black/70 hover:bg-black/90"} text-white border-none rounded-lg cursor-pointer flex items-center justify-center w-8 h-8 transition-colors duration-200 backdrop-blur-md ${isDark ? "shadow-[0_2px_8px_rgba(0,0,0,0.3)]" : "shadow-[0_2px_8px_rgba(0,0,0,0.2)]"} relative`,
        onMouseEnter: () => setIsButtonHovered(true),
        onMouseLeave: () => setIsButtonHovered(false),
        onClick,
        "aria-label": label
      },
      /* @__PURE__ */ React4.createElement(
        "svg",
        {
          xmlns: "http://www.w3.org/2000/svg",
          width: "16",
          height: "16",
          viewBox: "0 0 24 24",
          fill: "none",
          stroke: "currentColor",
          strokeWidth: "2",
          strokeLinecap: "round",
          strokeLinejoin: "round",
          className: "block"
        },
        icon
      ),
      showLabels && /* @__PURE__ */ React4.createElement(
        "span",
        {
          className: `${tooltipClasses.join(" ")} ${isButtonHovered ? "opacity-100" : "opacity-0"}`
        },
        label
      )
    );
  }, "IconButton");
  const formatValue = /* @__PURE__ */ __name((value) => {
    if (value === null) return "null";
    if (value === void 0) return "undefined";
    if (typeof value === "object") {
      try {
        return JSON.stringify(value, null, 2);
      } catch {
        return String(value);
      }
    }
    return String(value);
  }, "formatValue");
  const formatUserAgent = /* @__PURE__ */ __name((ua) => {
    if (!ua) return "N/A";
    return `${ua.device?.type || "unknown"}`;
  }, "formatUserAgent");
  const formatSafeArea = /* @__PURE__ */ __name((sa) => {
    if (!sa?.insets) return "N/A";
    const { top, bottom, left, right } = sa.insets;
    return `T:${top} B:${bottom} L:${left} R:${right}`;
  }, "formatSafeArea");
  return /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(
    "div",
    {
      ref: containerRef,
      className: `${className} relative h-fit`,
      onMouseEnter: () => !attachTo && setIsHovered(true),
      onMouseLeave: () => !attachTo && setIsHovered(false)
    },
    /* @__PURE__ */ React4.createElement(
      "div",
      {
        className: getPositionClasses().join(" "),
        style: getPositionOffsetStyles()
      },
      !isInInspector && /* @__PURE__ */ React4.createElement(React4.Fragment, null, !isFullscreen && !isPip && /* @__PURE__ */ React4.createElement(React4.Fragment, null, (viewControls === true || viewControls === "fullscreen") && /* @__PURE__ */ React4.createElement(IconButton, { onClick: handleFullscreen, label: "Fullscreen" }, /* @__PURE__ */ React4.createElement("path", { d: "M15 3h6v6" }), /* @__PURE__ */ React4.createElement("path", { d: "m21 3-7 7" }), /* @__PURE__ */ React4.createElement("path", { d: "m3 21 7-7" }), /* @__PURE__ */ React4.createElement("path", { d: "M9 21H3v-6" })), (viewControls === true || viewControls === "pip") && /* @__PURE__ */ React4.createElement(IconButton, { onClick: handlePip, label: "Picture in Picture" }, /* @__PURE__ */ React4.createElement("path", { d: "M21 9V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v10c0 1.1.9 2 2 2h4" }), /* @__PURE__ */ React4.createElement("rect", { width: "10", height: "7", x: "12", y: "13", rx: "2" }))), enableDebugger && /* @__PURE__ */ React4.createElement(IconButton, { onClick: handleToggleOverlay, label: "Debug Info" }, /* @__PURE__ */ React4.createElement("path", { d: "M12 20v-9" }), /* @__PURE__ */ React4.createElement("path", { d: "M14 7a4 4 0 0 1 4 4v3a6 6 0 0 1-12 0v-3a4 4 0 0 1 4-4z" }), /* @__PURE__ */ React4.createElement("path", { d: "M14.12 3.88 16 2" }), /* @__PURE__ */ React4.createElement("path", { d: "M21 21a4 4 0 0 0-3.81-4" }), /* @__PURE__ */ React4.createElement("path", { d: "M21 5a4 4 0 0 1-3.55 3.97" }), /* @__PURE__ */ React4.createElement("path", { d: "M22 13h-4" }), /* @__PURE__ */ React4.createElement("path", { d: "M3 21a4 4 0 0 1 3.81-4" }), /* @__PURE__ */ React4.createElement("path", { d: "M3 5a4 4 0 0 0 3.55 3.97" }), /* @__PURE__ */ React4.createElement("path", { d: "M6 13H2" }), /* @__PURE__ */ React4.createElement("path", { d: "m8 2 1.88 1.88" }), /* @__PURE__ */ React4.createElement("path", { d: "M9 7.13V6a3 3 0 1 1 6 0v1.13" })))
    ),
    children
  ), isOverlayOpen && enableDebugger && /* @__PURE__ */ React4.createElement(
    "div",
    {
      ref: overlayRef,
      className: "fixed inset-0 bg-black text-white font-mono text-xs z-[10000] overflow-auto p-4",
      onClick: (e) => {
        if (e.target === overlayRef.current) {
          setIsOverlayOpen(false);
        }
      }
    },
    /* @__PURE__ */ React4.createElement(
      "button",
      {
        onClick: () => setIsOverlayOpen(false),
        className: "absolute top-4 right-4 bg-white/10 text-white border-none rounded w-8 h-8 cursor-pointer flex items-center justify-center text-lg leading-none",
        "aria-label": "Close"
      },
      "\xD7"
    ),
    /* @__PURE__ */ React4.createElement("div", { className: "max-w-[1200px] mx-auto pt-10" }, /* @__PURE__ */ React4.createElement("h1", { className: "text-lg font-bold mb-4 border-b border-gray-700 pb-2" }, "Debug Info"), /* @__PURE__ */ React4.createElement("table", { className: "w-full border-collapse border-spacing-0" }, /* @__PURE__ */ React4.createElement("tbody", null, /* @__PURE__ */ React4.createElement("tr", { className: "border-b border-gray-700" }, /* @__PURE__ */ React4.createElement("td", { className: "p-2 font-bold w-[200px] align-top" }, "Props"), /* @__PURE__ */ React4.createElement("td", { className: "p-2 whitespace-pre-wrap break-all" }, formatValue(props))), /* @__PURE__ */ React4.createElement("tr", { className: "border-b border-gray-700" }, /* @__PURE__ */ React4.createElement("td", { className: "p-2 font-bold w-[200px] align-top" }, "Output"), /* @__PURE__ */ React4.createElement("td", { className: "p-2 whitespace-pre-wrap break-all" }, formatValue(output))), /* @__PURE__ */ React4.createElement("tr", { className: "border-b border-gray-700" }, /* @__PURE__ */ React4.createElement("td", { className: "p-2 font-bold w-[200px] align-top" }, "Metadata"), /* @__PURE__ */ React4.createElement("td", { className: "p-2 whitespace-pre-wrap break-all" }, formatValue(metadata))), /* @__PURE__ */ React4.createElement("tr", { className: "border-b border-gray-700" }, /* @__PURE__ */ React4.createElement("td", { className: "p-2 font-bold w-[200px] align-top" }, "State"), /* @__PURE__ */ React4.createElement("td", { className: "p-2 whitespace-pre-wrap break-all" }, formatValue(state))), /* @__PURE__ */ React4.createElement("tr", { className: "border-b border-gray-700" }, /* @__PURE__ */ React4.createElement("td", { className: "p-2 font-bold w-[200px] align-top" }, "Theme"), /* @__PURE__ */ React4.createElement("td", { className: "p-2" }, theme)), /* @__PURE__ */ React4.createElement("tr", { className: "border-b border-gray-700" }, /* @__PURE__ */ React4.createElement("td", { className: "p-2 font-bold w-[200px] align-top" }, "Display Mode"), /* @__PURE__ */ React4.createElement("td", { className: "p-2" }, displayMode)), /* @__PURE__ */ React4.createElement("tr", { className: "border-b border-gray-700" }, /* @__PURE__ */ React4.createElement("td", { className: "p-2 font-bold w-[200px] align-top" }, "Locale"), /* @__PURE__ */ React4.createElement("td", { className: "p-2" }, locale)), /* @__PURE__ */ React4.createElement("tr", { className: "border-b border-gray-700" }, /* @__PURE__ */ React4.createElement("td", { className: "p-2 font-bold w-[200px] align-top" }, "Max Height"), /* @__PURE__ */ React4.createElement("td", { className: "p-2" }, maxHeight, "px")), /* @__PURE__ */ React4.createElement("tr", { className: "border-b border-gray-700" }, /* @__PURE__ */ React4.createElement("td", { className: "p-2 font-bold w-[200px] align-top" }, "User Agent"), /* @__PURE__ */ React4.createElement("td", { className: "p-2" }, formatUserAgent(userAgent))), /* @__PURE__ */ React4.createElement("tr", { className: "border-b border-gray-700" }, /* @__PURE__ */ React4.createElement("td", { className: "p-2 font-bold w-[200px] align-top" }, "Safe Area"), /* @__PURE__ */ React4.createElement("td", { className: "p-2" }, formatSafeArea(safeArea))), /* @__PURE__ */ React4.createElement("tr", { className: "border-b border-gray-700" }, /* @__PURE__ */ React4.createElement("td", { className: "p-2 font-bold w-[200px] align-top" }, "API Available"), /* @__PURE__ */ React4.createElement("td", { className: "p-2" }, isAvailable ? "Yes" : "No")), /* @__PURE__ */ React4.createElement("tr", { className: "border-b border-gray-700" }, /* @__PURE__ */ React4.createElement("td", { className: "p-2 font-bold w-[200px] align-top" }, "window.openai Keys"), /* @__PURE__ */ React4.createElement("td", { className: "p-2" }, windowOpenAiKeys.length > 0 ? windowOpenAiKeys.join(", ") : "N/A")))), /* @__PURE__ */ React4.createElement("h2", { className: "text-base font-bold mt-8 mb-4 border-b border-gray-700 pb-2" }, "Actions"), /* @__PURE__ */ React4.createElement("div", { className: "flex flex-col gap-3" }, /* @__PURE__ */ React4.createElement("div", { className: "flex gap-2 items-center" }, /* @__PURE__ */ React4.createElement(
      "input",
      {
        type: "text",
        value: toolName,
        onChange: (e) => setToolName(e.target.value),
        placeholder: "Tool name",
        className: "py-1.5 px-2 bg-[#1a1a1a] text-white border border-gray-700 rounded font-mono text-xs w-[150px]"
      }
    ), /* @__PURE__ */ React4.createElement(
      "input",
      {
        type: "text",
        value: toolArgs,
        onChange: (e) => setToolArgs(e.target.value),
        placeholder: '{"key": "value"}',
        className: "py-1.5 px-2 bg-[#1a1a1a] text-white border border-gray-700 rounded font-mono text-xs flex-1"
      }
    ), /* @__PURE__ */ React4.createElement(
      "button",
      {
        onClick: handleCallTool,
        className: "py-1.5 px-3 bg-gray-800 text-white border border-gray-600 rounded cursor-pointer font-mono text-xs"
      },
      "Call Tool"
    )), /* @__PURE__ */ React4.createElement("div", { className: "flex gap-2 items-center" }, /* @__PURE__ */ React4.createElement(
      "input",
      {
        type: "text",
        value: followUpMessage,
        onChange: (e) => setFollowUpMessage(e.target.value),
        placeholder: "Follow-up message",
        className: "py-1.5 px-2 bg-[#1a1a1a] text-white border border-gray-700 rounded font-mono text-xs flex-1"
      }
    ), /* @__PURE__ */ React4.createElement(
      "button",
      {
        onClick: handleSendFollowUpMessage,
        className: "py-1.5 px-3 bg-gray-800 text-white border border-gray-600 rounded cursor-pointer font-mono text-xs"
      },
      "Send Follow-Up"
    )), /* @__PURE__ */ React4.createElement("div", { className: "flex gap-2 items-center" }, /* @__PURE__ */ React4.createElement(
      "input",
      {
        type: "text",
        value: externalUrl,
        onChange: (e) => setExternalUrl(e.target.value),
        placeholder: "External URL",
        className: "py-1.5 px-2 bg-[#1a1a1a] text-white border border-gray-700 rounded font-mono text-xs flex-1"
      }
    ), /* @__PURE__ */ React4.createElement(
      "button",
      {
        onClick: handleOpenExternal,
        className: "py-1.5 px-3 bg-gray-800 text-white border border-gray-600 rounded cursor-pointer font-mono text-xs"
      },
      "Open Link"
    )), /* @__PURE__ */ React4.createElement("div", { className: "flex gap-2 items-center" }, /* @__PURE__ */ React4.createElement("span", { className: "w-[150px] text-xs" }, "Display Mode:"), /* @__PURE__ */ React4.createElement(
      "button",
      {
        onClick: () => handleRequestDisplayMode("inline"),
        className: "py-1.5 px-3 bg-gray-800 text-white border border-gray-600 rounded cursor-pointer font-mono text-xs flex-1"
      },
      "Inline"
    ), /* @__PURE__ */ React4.createElement(
      "button",
      {
        onClick: () => handleRequestDisplayMode("pip"),
        className: "py-1.5 px-3 bg-gray-800 text-white border border-gray-600 rounded cursor-pointer font-mono text-xs flex-1"
      },
      "PiP"
    ), /* @__PURE__ */ React4.createElement(
      "button",
      {
        onClick: () => handleRequestDisplayMode("fullscreen"),
        className: "py-1.5 px-3 bg-gray-800 text-white border border-gray-600 rounded cursor-pointer font-mono text-xs flex-1"
      },
      "Fullscreen"
    )), /* @__PURE__ */ React4.createElement("div", { className: "flex gap-2 items-center" }, /* @__PURE__ */ React4.createElement(
      "button",
      {
        onClick: handleSetState,
        className: "py-1.5 px-3 bg-gray-800 text-white border border-gray-600 rounded cursor-pointer font-mono text-xs"
      },
      "Set State (Add Timestamp)"
    )), actionResult && /* @__PURE__ */ React4.createElement("div", { className: "mt-2 p-2 bg-[#1a1a1a] border border-gray-700 rounded whitespace-pre-wrap break-all text-[11px] max-h-[200px] overflow-auto" }, /* @__PURE__ */ React4.createElement("div", { className: "font-bold mb-1 text-gray-400" }, "Result:"), actionResult, /* @__PURE__ */ React4.createElement(
      "button",
      {
        onClick: () => setActionResult(""),
        className: "mt-2 py-1 px-2 bg-gray-800 text-white border border-gray-600 rounded cursor-pointer font-mono text-[11px]"
      },
      "Clear"
    ))))
  ));
}
__name(WidgetControls, "WidgetControls");

// src/react/McpUseProvider.tsx
import React5, {
  StrictMode,
  useCallback as useCallback3,
  useEffect as useEffect5,
  useRef as useRef3,
  useState as useState5
} from "react";
function getBasename() {
  if (typeof window === "undefined") return "/";
  const path = window.location.pathname;
  const match = path.match(/^(\/inspector\/api\/dev-widget\/[^/]+)/);
  if (match) {
    return match[1];
  }
  return "/";
}
__name(getBasename, "getBasename");
var HEIGHT_DEBOUNCE_MS = 150;
var MIN_HEIGHT_CHANGE_PX = 5;
function McpUseProvider({
  children,
  debugger: enableDebugger = false,
  viewControls = false,
  autoSize = false
}) {
  const basename = getBasename();
  const containerRef = useRef3(null);
  const lastHeightRef = useRef3(0);
  const debounceTimeoutRef = useRef3(null);
  const notificationInProgressRef = useRef3(false);
  const [BrowserRouter, setBrowserRouter] = useState5(null);
  const [routerError, setRouterError] = useState5(null);
  const [isRouterLoading, setIsRouterLoading] = useState5(true);
  useEffect5(() => {
    let mounted = true;
    (async () => {
      try {
        const routerModule = await import("react-router");
        if (mounted) {
          setBrowserRouter(() => routerModule.BrowserRouter);
          setIsRouterLoading(false);
        }
      } catch (error) {
        if (mounted) {
          setRouterError(
            new Error(
              "\u274C react-router not installed!\n\nTo use MCP widgets with McpUseProvider, you need to install:\n\n  npm install react-router\n  # or\n  pnpm add react-router\n\nThis dependency is automatically included in projects created with 'create-mcp-use-app'."
            )
          );
          setIsRouterLoading(false);
        }
      }
    })();
    return () => {
      mounted = false;
    };
  }, []);
  const notifyHeight = useCallback3((height) => {
    if (typeof window !== "undefined" && window.openai?.notifyIntrinsicHeight) {
      notificationInProgressRef.current = true;
      window.openai.notifyIntrinsicHeight(height).then(() => {
        notificationInProgressRef.current = false;
      }).catch((error) => {
        notificationInProgressRef.current = false;
        console.error(
          "[McpUseProvider] Failed to notify intrinsic height:",
          error
        );
      });
    }
  }, []);
  const debouncedNotifyHeight = useCallback3(
    (height) => {
      if (debounceTimeoutRef.current) {
        clearTimeout(debounceTimeoutRef.current);
      }
      debounceTimeoutRef.current = setTimeout(() => {
        const heightDiff = Math.abs(height - lastHeightRef.current);
        if (heightDiff >= MIN_HEIGHT_CHANGE_PX && height > 0) {
          lastHeightRef.current = height;
          notifyHeight(height);
        }
      }, HEIGHT_DEBOUNCE_MS);
    },
    [notifyHeight]
  );
  useEffect5(() => {
    if (!autoSize) {
      return;
    }
    const container = containerRef.current;
    if (!container || typeof ResizeObserver === "undefined") {
      return;
    }
    const observer = new ResizeObserver((entries) => {
      if (notificationInProgressRef.current) {
        return;
      }
      for (const entry of entries) {
        const height = entry.contentRect.height;
        const scrollHeight = entry.target.scrollHeight;
        const intrinsicHeight = Math.max(height, scrollHeight);
        debouncedNotifyHeight(intrinsicHeight);
      }
    });
    observer.observe(container);
    const initialHeight = Math.max(
      container.offsetHeight,
      container.scrollHeight
    );
    if (initialHeight > 0) {
      debouncedNotifyHeight(initialHeight);
    }
    return () => {
      observer.disconnect();
      if (debounceTimeoutRef.current) {
        clearTimeout(debounceTimeoutRef.current);
        debounceTimeoutRef.current = null;
      }
      notificationInProgressRef.current = false;
    };
  }, [autoSize, debouncedNotifyHeight]);
  if (isRouterLoading) {
    return /* @__PURE__ */ React5.createElement(StrictMode, null, /* @__PURE__ */ React5.createElement(ThemeProvider, null, /* @__PURE__ */ React5.createElement("div", { style: { padding: "20px", textAlign: "center" } }, "Loading...")));
  }
  if (routerError) {
    throw routerError;
  }
  let content = children;
  content = /* @__PURE__ */ React5.createElement(ErrorBoundary, null, content);
  if (enableDebugger || viewControls) {
    content = /* @__PURE__ */ React5.createElement(WidgetControls, { debugger: enableDebugger, viewControls }, content);
  }
  if (BrowserRouter) {
    content = /* @__PURE__ */ React5.createElement(BrowserRouter, { basename }, content);
  }
  content = /* @__PURE__ */ React5.createElement(ThemeProvider, null, content);
  if (autoSize) {
    const containerStyle = {
      width: "100%",
      minHeight: 0
    };
    content = /* @__PURE__ */ React5.createElement("div", { ref: containerRef, style: containerStyle }, content);
  }
  return /* @__PURE__ */ React5.createElement(StrictMode, null, content);
}
__name(McpUseProvider, "McpUseProvider");

// src/react/McpClientProvider.tsx
import React6, {
  createContext,
  useCallback as useCallback4,
  useContext,
  useEffect as useEffect6,
  useMemo as useMemo3,
  useRef as useRef4,
  useState as useState6
} from "react";
var McpClientContext = createContext(null);
var MAX_NOTIFICATIONS = 500;
function McpServerWrapper({
  id,
  options,
  onUpdate,
  rpcWrapTransport,
  onGlobalSamplingRequest,
  onGlobalElicitationRequest
}) {
  const {
    name,
    onSamplingRequest,
    onElicitationRequest,
    onNotificationReceived,
    wrapTransport: optionsWrapTransport
  } = options;
  const mcpOptions = useMemo3(() => {
    const {
      name: _name,
      onSamplingRequest: _onSamplingRequest,
      onElicitationRequest: _onElicitationRequest,
      onNotificationReceived: _onNotificationReceived,
      wrapTransport: _wrapTransport,
      ...rest
    } = options;
    return rest;
  }, [options]);
  const combinedWrapTransport = useMemo3(() => {
    if (!rpcWrapTransport && !optionsWrapTransport) return void 0;
    return (transport) => {
      let wrapped = transport;
      if (rpcWrapTransport) {
        wrapped = rpcWrapTransport(wrapped, id);
      }
      if (optionsWrapTransport) {
        wrapped = optionsWrapTransport(wrapped, id);
      }
      return wrapped;
    };
  }, [rpcWrapTransport, optionsWrapTransport, id]);
  const [notifications, setNotifications] = useState6([]);
  const [pendingSamplingRequests, setPendingSamplingRequests] = useState6([]);
  const samplingIdCounter = useRef4(0);
  const [pendingElicitationRequests, setPendingElicitationRequests] = useState6([]);
  const elicitationIdCounter = useRef4(0);
  const markNotificationRead = useCallback4((notificationId) => {
    setNotifications(
      (prev) => prev.map((n) => n.id === notificationId ? { ...n, read: true } : n)
    );
  }, []);
  const markAllNotificationsRead = useCallback4(() => {
    setNotifications((prev) => prev.map((n) => ({ ...n, read: true })));
  }, []);
  const clearNotifications = useCallback4(() => {
    setNotifications([]);
  }, []);
  const handleNotification = useCallback4(
    (notification) => {
      const mcpNotification = {
        id: globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random()}`,
        method: notification.method,
        params: notification.params,
        timestamp: Date.now(),
        read: false
      };
      setNotifications((prev) => {
        const updated = [mcpNotification, ...prev];
        if (updated.length > MAX_NOTIFICATIONS) {
          return updated.slice(0, MAX_NOTIFICATIONS);
        }
        return updated;
      });
      onNotificationReceived?.(mcpNotification);
    },
    [onNotificationReceived]
  );
  const approveSampling = useCallback4(
    (requestId, result) => {
      setPendingSamplingRequests((prev) => {
        const request = prev.find((r) => r.id === requestId);
        if (request) {
          request.resolve(result);
          return prev.filter((r) => r.id !== requestId);
        }
        return prev;
      });
    },
    []
  );
  const rejectSampling = useCallback4((requestId, error) => {
    setPendingSamplingRequests((prev) => {
      const request = prev.find((r) => r.id === requestId);
      if (request) {
        request.reject(new Error(error || "User rejected sampling request"));
        return prev.filter((r) => r.id !== requestId);
      }
      return prev;
    });
  }, []);
  const samplingCallback = useCallback4(
    async (params) => {
      return new Promise((resolve, reject) => {
        const requestId = `sampling-${samplingIdCounter.current++}`;
        const request = {
          id: requestId,
          request: { method: "sampling/createMessage", params },
          timestamp: Date.now(),
          serverName: name || id
        };
        const newRequest = {
          ...request,
          resolve,
          reject
        };
        setPendingSamplingRequests((prev) => [...prev, newRequest]);
        onSamplingRequest?.(request);
        onGlobalSamplingRequest?.(
          request,
          id,
          name || id,
          approveSampling,
          rejectSampling
        );
      });
    },
    [
      id,
      name,
      onSamplingRequest,
      onGlobalSamplingRequest,
      approveSampling,
      rejectSampling
    ]
  );
  const approveElicitation = useCallback4(
    (requestId, result) => {
      setPendingElicitationRequests((prev) => {
        const request = prev.find((r) => r.id === requestId);
        if (request) {
          request.resolve(result);
          return prev.filter((r) => r.id !== requestId);
        }
        return prev;
      });
    },
    []
  );
  const rejectElicitation = useCallback4((requestId, error) => {
    setPendingElicitationRequests((prev) => {
      const request = prev.find((r) => r.id === requestId);
      if (request) {
        request.reject(new Error(error || "User rejected elicitation request"));
        return prev.filter((r) => r.id !== requestId);
      }
      return prev;
    });
  }, []);
  const elicitationCallback = useCallback4(
    async (params) => {
      return new Promise((resolve, reject) => {
        const requestId = `elicitation-${elicitationIdCounter.current++}`;
        const request = {
          id: requestId,
          request: params,
          timestamp: Date.now(),
          serverName: name || id
        };
        const newRequest = {
          ...request,
          resolve,
          reject
        };
        setPendingElicitationRequests((prev) => [...prev, newRequest]);
        onElicitationRequest?.(request);
        onGlobalElicitationRequest?.(
          request,
          id,
          name || id,
          approveElicitation,
          rejectElicitation
        );
      });
    },
    [
      id,
      name,
      onElicitationRequest,
      onGlobalElicitationRequest,
      approveElicitation,
      rejectElicitation
    ]
  );
  const mcp = useMcp({
    ...mcpOptions,
    onNotification: handleNotification,
    samplingCallback,
    onElicitation: elicitationCallback,
    wrapTransport: combinedWrapTransport
  });
  const publicSamplingRequests = useMemo3(
    () => pendingSamplingRequests.map((r) => ({
      id: r.id,
      request: r.request,
      timestamp: r.timestamp,
      serverName: r.serverName
    })),
    [pendingSamplingRequests]
  );
  const publicElicitationRequests = useMemo3(
    () => pendingElicitationRequests.map((r) => ({
      id: r.id,
      request: r.request,
      timestamp: r.timestamp,
      serverName: r.serverName
    })),
    [pendingElicitationRequests]
  );
  const unreadNotificationCount = useMemo3(
    () => notifications.filter((n) => !n.read).length,
    [notifications]
  );
  const onUpdateRef = useRef4(onUpdate);
  const prevServerRef = useRef4(null);
  useEffect6(() => {
    onUpdateRef.current = onUpdate;
  }, [onUpdate]);
  useEffect6(() => {
    const server = {
      ...mcp,
      id,
      url: options.url || "",
      name: name || id,
      notifications,
      unreadNotificationCount,
      markNotificationRead,
      markAllNotificationsRead,
      clearNotifications,
      pendingSamplingRequests: publicSamplingRequests,
      approveSampling,
      rejectSampling,
      pendingElicitationRequests: publicElicitationRequests,
      approveElicitation,
      rejectElicitation
    };
    const prevServer = prevServerRef.current;
    if (!prevServer || prevServer.state !== server.state || prevServer.error !== server.error || prevServer.authUrl !== server.authUrl || prevServer.tools.length !== server.tools.length || prevServer.resources.length !== server.resources.length || prevServer.prompts.length !== server.prompts.length || prevServer.serverInfo !== server.serverInfo || prevServer.capabilities !== server.capabilities || prevServer.notifications.length !== server.notifications.length || prevServer.unreadNotificationCount !== server.unreadNotificationCount || prevServer.pendingSamplingRequests.length !== server.pendingSamplingRequests.length || prevServer.pendingElicitationRequests.length !== server.pendingElicitationRequests.length || !prevServer.client) {
      prevServerRef.current = server;
      onUpdateRef.current(server);
    }
  }, [
    id,
    name,
    options.url,
    // Primitive values that indicate meaningful state changes
    mcp.state,
    mcp.error,
    mcp.authUrl,
    // Use array LENGTHS instead of array references to avoid triggering on reference changes
    mcp.tools.length,
    mcp.resources.length,
    mcp.resourceTemplates.length,
    mcp.prompts.length,
    // Objects excluded - manual comparison handles serverInfo/capabilities changes
    // Functions excluded - they're stable via useCallback in useMcp
    // mcp.log excluded - log changes shouldn't trigger provider updates
    // mcp.client excluded - client reference stability handled by manual check
    notifications.length,
    unreadNotificationCount,
    publicSamplingRequests.length,
    publicElicitationRequests.length,
    // Callback functions are stable via useCallback
    markNotificationRead,
    markAllNotificationsRead,
    clearNotifications,
    approveSampling,
    rejectSampling,
    approveElicitation,
    rejectElicitation
  ]);
  return null;
}
__name(McpServerWrapper, "McpServerWrapper");
function McpClientProvider({
  children,
  mcpServers,
  storageProvider,
  enableRpcLogging = false,
  onServerAdded,
  onServerRemoved,
  onServerStateChange,
  onSamplingRequest,
  onElicitationRequest
}) {
  const [serverConfigs, setServerConfigs] = useState6([]);
  const [servers, setServers] = useState6([]);
  const [storageLoaded, setStorageLoaded] = useState6(false);
  const [rpcWrapTransport, setRpcWrapTransport] = useState6(void 0);
  const [rpcLoggingReady, setRpcLoggingReady] = useState6(false);
  useEffect6(() => {
    if (!enableRpcLogging || typeof window === "undefined") {
      setRpcWrapTransport(void 0);
      setRpcLoggingReady(true);
      return;
    }
    import("./rpc-logger-INMU527Q.js").then((module) => {
      console.log("[McpClientProvider] RPC logger loaded");
      setRpcWrapTransport(() => module.wrapTransportForLogging);
      setRpcLoggingReady(true);
    }).catch((err) => {
      console.error("[McpClientProvider] Failed to load RPC logger:", err);
      setRpcWrapTransport(void 0);
      setRpcLoggingReady(true);
    });
  }, [enableRpcLogging]);
  useEffect6(() => {
    if (!rpcLoggingReady) {
      console.log(
        "[McpClientProvider] Waiting for RPC logging to be ready before loading servers"
      );
      return;
    }
    const loadServers = /* @__PURE__ */ __name(async () => {
      console.log(
        "[McpClientProvider] Loading servers, storageProvider:",
        !!storageProvider,
        "mcpServers:",
        mcpServers
      );
      if (!storageProvider) {
        if (mcpServers) {
          const configs = Object.entries(mcpServers).map(([id, options]) => ({
            id,
            options
          }));
          console.log(
            "[McpClientProvider] Loaded from mcpServers prop:",
            configs.length
          );
          setServerConfigs(configs);
        }
        setStorageLoaded(true);
        return;
      }
      try {
        const storedServers = await Promise.resolve(
          storageProvider.getServers()
        );
        console.log(
          "[McpClientProvider] Loaded from storage:",
          Object.keys(storedServers).length
        );
        const mergedServers = { ...storedServers, ...mcpServers };
        const configs = Object.entries(mergedServers).map(([id, options]) => ({
          id,
          options
        }));
        console.log(
          "[McpClientProvider] Total servers after merge:",
          configs.length
        );
        setServerConfigs(configs);
        setStorageLoaded(true);
      } catch (error) {
        console.error(
          "[McpClientProvider] Failed to load from storage:",
          error
        );
        if (mcpServers) {
          const configs = Object.entries(mcpServers).map(([id, options]) => ({
            id,
            options
          }));
          setServerConfigs(configs);
        }
        setStorageLoaded(true);
      }
    }, "loadServers");
    loadServers();
  }, [storageProvider, mcpServers, rpcLoggingReady]);
  useEffect6(() => {
    if (!storageProvider || !storageLoaded) return;
    const saveServers = /* @__PURE__ */ __name(async () => {
      try {
        const serversToSave = serverConfigs.reduce(
          (acc, config) => {
            acc[config.id] = config.options;
            return acc;
          },
          {}
        );
        await Promise.resolve(storageProvider.setServers(serversToSave));
      } catch (error) {
        console.error("[McpClientProvider] Failed to save to storage:", error);
      }
    }, "saveServers");
    saveServers();
  }, [serverConfigs, storageProvider, storageLoaded]);
  const handleServerUpdate = useCallback4(
    (updatedServer) => {
      setServers((prev) => {
        const index = prev.findIndex((s) => s.id === updatedServer.id);
        const isNewServer = index === -1;
        if (isNewServer) {
          onServerAdded?.(updatedServer.id, updatedServer);
          return [...prev, updatedServer];
        }
        const current = prev[index];
        const stateChanged = current.state !== updatedServer.state;
        if (current.state === updatedServer.state && current.tools === updatedServer.tools && current.resources === updatedServer.resources && current.prompts === updatedServer.prompts && current.error === updatedServer.error && current.serverInfo === updatedServer.serverInfo && current.client === updatedServer.client && current.notifications === updatedServer.notifications && current.unreadNotificationCount === updatedServer.unreadNotificationCount && current.pendingSamplingRequests.length === updatedServer.pendingSamplingRequests.length && current.pendingElicitationRequests.length === updatedServer.pendingElicitationRequests.length) {
          return prev;
        }
        if (stateChanged) {
          onServerStateChange?.(updatedServer.id, updatedServer.state);
        }
        const newServers = [...prev];
        newServers[index] = updatedServer;
        return newServers;
      });
    },
    [onServerAdded, onServerStateChange]
  );
  const addServer = useCallback4((id, options) => {
    console.log("[McpClientProvider] addServer called:", id, options);
    setServerConfigs((prev) => {
      if (prev.find((s) => s.id === id)) {
        console.warn(
          `[McpClientProvider] Server with id "${id}" already exists`
        );
        return prev;
      }
      console.log("[McpClientProvider] Adding new server to configs:", id);
      return [...prev, { id, options }];
    });
  }, []);
  const removeServer = useCallback4(
    (id) => {
      setServers((prev) => {
        const server = prev.find((s) => s.id === id);
        if (server?.disconnect) {
          server.disconnect();
        }
        if (server?.clearStorage) {
          server.clearStorage();
        }
        return prev.filter((s) => s.id !== id);
      });
      setServerConfigs((prev) => prev.filter((s) => s.id !== id));
      onServerRemoved?.(id);
    },
    [onServerRemoved]
  );
  const updateServer = useCallback4(
    async (id, options) => {
      return new Promise((resolve) => {
        const currentConfig = serverConfigs.find((s) => s.id === id);
        if (!currentConfig) {
          console.warn(
            `[McpClientProvider] Cannot update server "${id}" - not found`
          );
          resolve();
          return;
        }
        const updatedOptions = {
          ...currentConfig.options,
          ...options
        };
        setServers((prev) => {
          const server = prev.find((s) => s.id === id);
          if (server?.disconnect) {
            server.disconnect();
          }
          if (server?.clearStorage) {
            server.clearStorage();
          }
          return prev.filter((s) => s.id !== id);
        });
        setServerConfigs((prev) => {
          const updated = prev.map(
            (s) => s.id === id ? { id, options: updatedOptions } : s
          );
          setTimeout(() => resolve(), 0);
          return updated;
        });
      });
    },
    [serverConfigs]
  );
  const getServer = useCallback4(
    (id) => {
      return servers.find((s) => s.id === id);
    },
    [servers]
  );
  const contextValue = useMemo3(
    () => ({
      servers,
      addServer,
      removeServer,
      updateServer,
      getServer,
      storageLoaded
    }),
    [servers, addServer, removeServer, updateServer, getServer, storageLoaded]
  );
  return /* @__PURE__ */ React6.createElement(McpClientContext.Provider, { value: contextValue }, children, serverConfigs.map((config) => /* @__PURE__ */ React6.createElement(
    McpServerWrapper,
    {
      key: config.id,
      id: config.id,
      options: config.options,
      onUpdate: handleServerUpdate,
      rpcWrapTransport,
      onGlobalSamplingRequest: onSamplingRequest,
      onGlobalElicitationRequest: onElicitationRequest
    }
  )));
}
__name(McpClientProvider, "McpClientProvider");
function useMcpClient() {
  const context = useContext(McpClientContext);
  if (!context) {
    throw new Error("useMcpClient must be used within a McpClientProvider");
  }
  return context;
}
__name(useMcpClient, "useMcpClient");
function useMcpServer(id) {
  const { getServer } = useMcpClient();
  return getServer(id);
}
__name(useMcpServer, "useMcpServer");

// src/react/storage/LocalStorageProvider.ts
var LocalStorageProvider = class {
  constructor(storageKey = "mcp-client-servers") {
    this.storageKey = storageKey;
  }
  static {
    __name(this, "LocalStorageProvider");
  }
  getServers() {
    try {
      const stored = localStorage.getItem(this.storageKey);
      return stored ? JSON.parse(stored) : {};
    } catch (error) {
      console.error("[LocalStorageProvider] Failed to load servers:", error);
      return {};
    }
  }
  setServers(servers) {
    try {
      localStorage.setItem(this.storageKey, JSON.stringify(servers));
    } catch (error) {
      console.error("[LocalStorageProvider] Failed to save servers:", error);
    }
  }
  setServer(id, config) {
    const servers = this.getServers();
    servers[id] = config;
    this.setServers(servers);
  }
  removeServer(id) {
    const servers = this.getServers();
    delete servers[id];
    this.setServers(servers);
  }
  clear() {
    try {
      localStorage.removeItem(this.storageKey);
    } catch (error) {
      console.error("[LocalStorageProvider] Failed to clear:", error);
    }
  }
};

// src/react/storage/MemoryStorageProvider.ts
var MemoryStorageProvider = class {
  static {
    __name(this, "MemoryStorageProvider");
  }
  storage = {};
  getServers() {
    return { ...this.storage };
  }
  setServers(servers) {
    this.storage = { ...servers };
  }
  setServer(id, config) {
    this.storage[id] = config;
  }
  removeServer(id) {
    delete this.storage[id];
  }
  clear() {
    this.storage = {};
  }
};

export {
  useMcp,
  ErrorBoundary,
  Image,
  useWidget,
  useWidgetProps,
  useWidgetTheme,
  useWidgetState,
  ThemeProvider,
  WidgetControls,
  McpUseProvider,
  McpClientProvider,
  useMcpClient,
  useMcpServer,
  LocalStorageProvider,
  MemoryStorageProvider
};