@mcp-use/client 2.0.0-beta.0 → 2.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/auth/browser.d.ts +8 -4
  3. package/dist/auth/browser.d.ts.map +1 -1
  4. package/dist/auth/session-store.d.ts +5 -0
  5. package/dist/auth/session-store.d.ts.map +1 -1
  6. package/dist/index-browser.d.ts +1 -1
  7. package/dist/index-browser.d.ts.map +1 -1
  8. package/dist/index-browser.js +49 -151
  9. package/dist/index-browser.js.map +1 -1
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +22 -161
  13. package/dist/index.js.map +1 -1
  14. package/dist/react/McpClientProvider.d.ts.map +1 -1
  15. package/dist/react/index.js +309 -215
  16. package/dist/react/index.js.map +1 -1
  17. package/dist/react/types.d.ts +9 -3
  18. package/dist/react/types.d.ts.map +1 -1
  19. package/dist/react/useMcp-helpers.d.ts.map +1 -1
  20. package/dist/react/useMcp-operations.d.ts.map +1 -1
  21. package/dist/react/useMcp.d.ts.map +1 -1
  22. package/dist/react/view/ViewRenderer.d.ts +2 -1
  23. package/dist/react/view/ViewRenderer.d.ts.map +1 -1
  24. package/dist/react/view/inject-openai-file-apis.d.ts +6 -0
  25. package/dist/react/view/inject-openai-file-apis.d.ts.map +1 -0
  26. package/dist/react/view/types.d.ts +16 -0
  27. package/dist/react/view/types.d.ts.map +1 -1
  28. package/dist/react/view/use-display-mode.d.ts +1 -0
  29. package/dist/react/view/use-display-mode.d.ts.map +1 -1
  30. package/dist/telemetry/index.d.ts +1 -1
  31. package/dist/telemetry/index.d.ts.map +1 -1
  32. package/dist/telemetry/tel-fetch.d.ts +0 -6
  33. package/dist/telemetry/tel-fetch.d.ts.map +1 -1
  34. package/dist/telemetry/telemetry-node.d.ts.map +1 -1
  35. package/dist/telemetry/telemetry.d.ts +0 -8
  36. package/dist/telemetry/telemetry.d.ts.map +1 -1
  37. package/dist/transport/http.d.ts +6 -6
  38. package/package.json +2 -2
@@ -1473,7 +1473,7 @@ var HttpConnector = class extends BaseConnector {
1473
1473
  };
1474
1474
 
1475
1475
  // src/utils/version.ts
1476
- var VERSION = "2.0.0-beta.0";
1476
+ var VERSION = "2.0.0-beta.9";
1477
1477
  function getPackageVersion() {
1478
1478
  return VERSION;
1479
1479
  }
@@ -1849,6 +1849,14 @@ var OAuthSessionStore = class _OAuthSessionStore {
1849
1849
  async getTokenEndpoint() {
1850
1850
  return (await this.discoveryState())?.authorizationServerMetadata?.token_endpoint ?? null;
1851
1851
  }
1852
+ /**
1853
+ * Return the protected-resource URL selected during OAuth discovery.
1854
+ * Consumers can persist it and reuse it for server-side refresh exchanges.
1855
+ */
1856
+ async getResource() {
1857
+ const resource = (await this.discoveryState())?.resourceMetadata?.resource;
1858
+ return typeof resource === "string" ? resource : null;
1859
+ }
1852
1860
  };
1853
1861
 
1854
1862
  // src/auth/browser.ts
@@ -1966,20 +1974,19 @@ var BrowserOAuthClientProvider = class {
1966
1974
  * therefore never alters fetch behavior for other servers, other
1967
1975
  * connections, or the rest of the page.
1968
1976
  *
1969
- * When this provider is not configured to proxy OAuth requests (no
1970
- * `oauthProxyUrl`, or `proxyOAuthRequests` disabled), the provided
1971
- * `baseFetch` is returned as-is (or `undefined` when none is given, letting
1972
- * the SDK fall back to its default `fetch`).
1977
+ * OAuth metadata is always fetched with `cache: "no-store"`, including in
1978
+ * direct mode. Authorization servers commonly vary CORS headers by Origin;
1979
+ * bypassing the browser HTTP cache prevents a revalidated response cached
1980
+ * for another localhost origin from poisoning discovery. When OAuth proxying
1981
+ * is disabled or no `oauthProxyUrl` is configured, all requests still go
1982
+ * directly to their original URLs.
1973
1983
  *
1974
1984
  * @param baseFetch - The fetch used for non-OAuth requests and for the
1975
1985
  * underlying proxy calls. Defaults to the global `fetch`.
1976
1986
  */
1977
1987
  getProxyFetch(baseFetch) {
1978
- if (!this.proxyOAuthRequests || !this.oauthProxyUrl) {
1979
- return baseFetch;
1980
- }
1981
1988
  const base = baseFetch ?? globalThis.fetch.bind(globalThis);
1982
- const oauthProxyUrl = this.oauthProxyUrl;
1989
+ const oauthProxyUrl = this.proxyOAuthRequests && this.oauthProxyUrl ? this.oauthProxyUrl : void 0;
1983
1990
  const discoveredEndpoints = /* @__PURE__ */ new Set();
1984
1991
  let restoredDiscovery = false;
1985
1992
  return async (input, init) => {
@@ -1992,6 +1999,12 @@ var BrowserOAuthClientProvider = class {
1992
1999
  return await base(input, init);
1993
2000
  }
1994
2001
  const isMetadata = pathname.includes("/.well-known/");
2002
+ if (!oauthProxyUrl) {
2003
+ return await base(
2004
+ isMetadata ? url : input,
2005
+ isMetadata ? { ...init, cache: "no-store" } : init
2006
+ );
2007
+ }
1995
2008
  if (!restoredDiscovery) {
1996
2009
  restoredDiscovery = true;
1997
2010
  const metadata = (await this.discoveryState())?.authorizationServerMetadata;
@@ -2026,7 +2039,8 @@ var BrowserOAuthClientProvider = class {
2026
2039
  if (isMetadata) {
2027
2040
  const response2 = await base(proxyEndpoint, {
2028
2041
  ...init,
2029
- method: "GET"
2042
+ method: "GET",
2043
+ cache: "no-store"
2030
2044
  });
2031
2045
  try {
2032
2046
  const metadata = await response2.clone().json();
@@ -2083,7 +2097,16 @@ var BrowserOAuthClientProvider = class {
2083
2097
  }
2084
2098
  async saveClientInformation(clientInformation, ctx) {
2085
2099
  if (this.staticClientInfo) return;
2086
- return this.session.saveClientInformation(clientInformation, ctx);
2100
+ const { client_secret: discardedClientSecret, ...publicClientInformation } = clientInformation;
2101
+ if (discardedClientSecret) {
2102
+ console.info(
2103
+ `[${this.storageKeyPrefix}] Discarded client_secret returned for a public browser OAuth client.`
2104
+ );
2105
+ }
2106
+ return this.session.saveClientInformation(
2107
+ publicClientInformation,
2108
+ ctx
2109
+ );
2087
2110
  }
2088
2111
  codeVerifier() {
2089
2112
  return this.session.codeVerifier();
@@ -2113,6 +2136,10 @@ var BrowserOAuthClientProvider = class {
2113
2136
  getTokenEndpoint() {
2114
2137
  return this.session.getTokenEndpoint();
2115
2138
  }
2139
+ /** Return the protected-resource URL selected during OAuth discovery. */
2140
+ getResource() {
2141
+ return this.session.getResource();
2142
+ }
2116
2143
  /**
2117
2144
  * Return the stored public OAuth client ID. Browser providers do not retain
2118
2145
  * client secrets.
@@ -2370,62 +2397,6 @@ async function telFetch(url, init) {
2370
2397
  }
2371
2398
  var POSTHOG_HOST = "https://eu.i.posthog.com";
2372
2399
  var POSTHOG_API_KEY = "phc_lyTtbYwvkdSbrcMQNPiKiiRWrrM1seyKIMjycSvItEI";
2373
- var SCARF_GATEWAY_URL = "https://mcpuse.gateway.scarf.sh/events-ts";
2374
- var SCARF_GATEWAY_BEACON_URL = "https://mcpuse.gateway.scarf.sh/simple/";
2375
- var SCARF_BEACON_MAX_URL = 1800;
2376
- var SCARF_BEACON_TRUNCATED_KEYS = /* @__PURE__ */ new Set(["query", "response", "messages"]);
2377
- function stringifyScarfValue(value) {
2378
- if (value === null || value === void 0) return "";
2379
- if (typeof value === "string") return value;
2380
- if (typeof value === "number" || typeof value === "boolean") {
2381
- return String(value);
2382
- }
2383
- try {
2384
- return JSON.stringify(value);
2385
- } catch {
2386
- return String(value);
2387
- }
2388
- }
2389
- function buildScarfBeaconUrl(properties, baseUrl = SCARF_GATEWAY_BEACON_URL) {
2390
- const params = new URLSearchParams();
2391
- for (const [key, value] of Object.entries(properties)) {
2392
- let serialized = stringifyScarfValue(value);
2393
- if (SCARF_BEACON_TRUNCATED_KEYS.has(key) && serialized.length > 120) {
2394
- serialized = `${serialized.slice(0, 117)}...`;
2395
- }
2396
- if (serialized !== "") params.set(key, serialized);
2397
- }
2398
- let url = `${baseUrl}${baseUrl.includes("?") ? "&" : "?"}${params.toString()}`;
2399
- if (url.length <= SCARF_BEACON_MAX_URL) return url;
2400
- const keys = [...params.keys()].sort(
2401
- (a, b) => (params.get(b)?.length ?? 0) - (params.get(a)?.length ?? 0)
2402
- );
2403
- for (const key of keys) {
2404
- if (url.length <= SCARF_BEACON_MAX_URL) break;
2405
- if (key === "event" || key === "user_id") continue;
2406
- params.delete(key);
2407
- url = `${baseUrl}${baseUrl.includes("?") ? "&" : "?"}${params.toString()}`;
2408
- }
2409
- return url.slice(0, SCARF_BEACON_MAX_URL);
2410
- }
2411
- function captureScarfPost(properties, endpoint) {
2412
- return telFetch(endpoint, {
2413
- method: "POST",
2414
- headers: { "Content-Type": "application/json" },
2415
- keepalive: true,
2416
- body: JSON.stringify(properties)
2417
- });
2418
- }
2419
- function captureScarfBeacon(properties, endpoint) {
2420
- try {
2421
- const url = buildScarfBeaconUrl(properties, endpoint);
2422
- const img = new Image();
2423
- img.referrerPolicy = "no-referrer-when-downgrade";
2424
- img.src = url;
2425
- } catch {
2426
- }
2427
- return Promise.resolve();
2428
- }
2429
2400
  function capturePostHog(params) {
2430
2401
  const host = params.host ?? POSTHOG_HOST;
2431
2402
  const apiKey = params.apiKey ?? POSTHOG_API_KEY;
@@ -2442,13 +2413,6 @@ function capturePostHog(params) {
2442
2413
  })
2443
2414
  });
2444
2415
  }
2445
- function captureScarf(properties, endpoint = SCARF_GATEWAY_URL) {
2446
- if (typeof window !== "undefined") {
2447
- const beaconEndpoint = endpoint === SCARF_GATEWAY_URL ? SCARF_GATEWAY_BEACON_URL : endpoint;
2448
- return captureScarfBeacon(properties, beaconEndpoint);
2449
- }
2450
- return captureScarfPost(properties, endpoint);
2451
- }
2452
2416
 
2453
2417
  // src/telemetry/telemetry.ts
2454
2418
  function generateUUID() {
@@ -2459,18 +2423,6 @@ function secureRandomString() {
2459
2423
  globalThis.crypto.getRandomValues(array);
2460
2424
  return Array.from(array, (v) => v.toString(16).padStart(2, "0")).join("");
2461
2425
  }
2462
- function isVersionGreater(a, b) {
2463
- const parse = (v) => v.split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
2464
- const pa = parse(a);
2465
- const pb = parse(b);
2466
- const len = Math.max(pa.length, pb.length);
2467
- for (let i = 0; i < len; i++) {
2468
- const da = pa[i] ?? 0;
2469
- const db = pb[i] ?? 0;
2470
- if (da !== db) return da > db;
2471
- }
2472
- return false;
2473
- }
2474
2426
  var USER_ID_STORAGE_KEY = "mcp_use_user_id";
2475
2427
  var PROJECT_API_KEY = "phc_lyTtbYwvkdSbrcMQNPiKiiRWrrM1seyKIMjycSvItEI";
2476
2428
  var HOST = "https://eu.i.posthog.com";
@@ -2499,10 +2451,7 @@ function createLocalStorageBackend() {
2499
2451
  localStorage.setItem(USER_ID_STORAGE_KEY, id);
2500
2452
  } catch {
2501
2453
  }
2502
- },
2503
- // Package-download dedup is node/Scarf-oriented; localStorage unused.
2504
- getDownloadedVersion: () => null,
2505
- setDownloadedVersion: () => void 0
2454
+ }
2506
2455
  };
2507
2456
  }
2508
2457
  function detectRuntimeEnvironment() {
@@ -2570,17 +2519,13 @@ var Telemetry = class _Telemetry {
2570
2519
  _currUserId = null;
2571
2520
  _telemetryEnabled = false;
2572
2521
  _pending = /* @__PURE__ */ new Set();
2573
- _scarfEnabled = false;
2574
2522
  _runtimeEnvironment;
2575
2523
  _storageCapability;
2576
2524
  _storage;
2577
- /** True when node entry installed fs storage (package-download dedup). */
2578
- _fsBacked;
2579
2525
  _source;
2580
2526
  _productVersion;
2581
2527
  constructor() {
2582
2528
  this._runtimeEnvironment = detectRuntimeEnvironment();
2583
- this._fsBacked = configuredStorage !== null;
2584
2529
  this._storage = configuredStorage ?? createLocalStorageBackend() ?? null;
2585
2530
  this._storageCapability = this._storage ? "persistent" : "session-only";
2586
2531
  this._source = readSourceHint() || this._runtimeEnvironment;
@@ -2599,14 +2544,6 @@ var Telemetry = class _Telemetry {
2599
2544
  "Anonymized telemetry enabled. Set MCP_USE_ANONYMIZED_TELEMETRY=false to disable."
2600
2545
  );
2601
2546
  this._telemetryEnabled = true;
2602
- this._scarfEnabled = true;
2603
- if (this._fsBacked) {
2604
- setTimeout(() => {
2605
- this.trackPackageDownload({ triggered_by: "initialization" }).catch(
2606
- (e) => logger.debug(`Failed to track package download: ${e}`)
2607
- );
2608
- }, 0);
2609
- }
2610
2547
  }
2611
2548
  }
2612
2549
  get runtimeEnvironment() {
@@ -2638,7 +2575,7 @@ var Telemetry = class _Telemetry {
2638
2575
  this._productVersion = version;
2639
2576
  }
2640
2577
  get isEnabled() {
2641
- return this._telemetryEnabled || this._scarfEnabled;
2578
+ return this._telemetryEnabled;
2642
2579
  }
2643
2580
  get userId() {
2644
2581
  if (this._currUserId) return this._currUserId;
@@ -2661,7 +2598,7 @@ var Telemetry = class _Telemetry {
2661
2598
  return this._currUserId;
2662
2599
  }
2663
2600
  async capture(event) {
2664
- if (!this._telemetryEnabled && !this._scarfEnabled) return;
2601
+ if (!this._telemetryEnabled) return;
2665
2602
  const currentUserId = this.userId;
2666
2603
  const properties = {
2667
2604
  ...event.properties,
@@ -2670,52 +2607,15 @@ var Telemetry = class _Telemetry {
2670
2607
  source: this._source,
2671
2608
  runtime: this._runtimeEnvironment
2672
2609
  };
2673
- if (this._telemetryEnabled) {
2674
- const p = capturePostHog({
2675
- host: HOST,
2676
- apiKey: PROJECT_API_KEY,
2677
- event: event.name,
2678
- distinctId: currentUserId,
2679
- properties
2680
- });
2681
- this._pending.add(p);
2682
- void p.finally(() => this._pending.delete(p));
2683
- }
2684
- if (this._scarfEnabled) {
2685
- const p = captureScarf({
2686
- ...properties,
2687
- user_id: currentUserId,
2688
- event: event.name
2689
- });
2690
- this._pending.add(p);
2691
- void p.finally(() => this._pending.delete(p));
2692
- }
2693
- }
2694
- async trackPackageDownload(properties) {
2695
- if (!this._scarfEnabled || !this._fsBacked || !this._storage) return;
2696
- const currentVersion = getPackageVersion();
2697
- const saved = this._storage.getDownloadedVersion();
2698
- let firstDownload = false;
2699
- let shouldTrack = false;
2700
- if (!saved) {
2701
- shouldTrack = true;
2702
- firstDownload = true;
2703
- this._storage.setDownloadedVersion(currentVersion);
2704
- } else if (isVersionGreater(currentVersion, saved)) {
2705
- shouldTrack = true;
2706
- this._storage.setDownloadedVersion(currentVersion);
2707
- }
2708
- if (!shouldTrack) return;
2709
- await captureScarf({
2710
- ...properties || {},
2711
- mcp_use_version: currentVersion,
2712
- user_id: this.userId,
2713
- event: "package_download",
2714
- first_download: firstDownload,
2715
- language: "typescript",
2716
- source: this._source,
2717
- runtime: this._runtimeEnvironment
2610
+ const p = capturePostHog({
2611
+ host: HOST,
2612
+ apiKey: PROJECT_API_KEY,
2613
+ event: event.name,
2614
+ distinctId: currentUserId,
2615
+ properties
2718
2616
  });
2617
+ this._pending.add(p);
2618
+ void p.finally(() => this._pending.delete(p));
2719
2619
  }
2720
2620
  async trackAgentExecution(data) {
2721
2621
  if (!this.isEnabled) return;
@@ -4142,9 +4042,13 @@ function createBrowserOAuthProvider(params) {
4142
4042
  function startConnectionHealthMonitoring(params) {
4143
4043
  let healthCheckInterval = null;
4144
4044
  let lastSuccessfulCheck = Date.now();
4045
+ let headProbeUnsupported = false;
4145
4046
  const healthCheckIntervalMs = params.healthCheckIntervalMs ?? 1e4;
4146
4047
  const healthCheckTimeoutMs = params.healthCheckTimeoutMs ?? 3e4;
4147
4048
  const checkConnectionHealth = async () => {
4049
+ if (headProbeUnsupported) {
4050
+ return;
4051
+ }
4148
4052
  if (!params.isMountedRef.current || params.stateRef.current !== "ready") {
4149
4053
  if (healthCheckInterval) {
4150
4054
  clearInterval(healthCheckInterval);
@@ -4163,6 +4067,15 @@ function startConnectionHealthMonitoring(params) {
4163
4067
  headers: { ...params.allHeaders, ...authHeaders },
4164
4068
  signal: AbortSignal.timeout(5e3)
4165
4069
  });
4070
+ if (response.status === 405 || response.status === 404) {
4071
+ headProbeUnsupported = true;
4072
+ lastSuccessfulCheck = Date.now();
4073
+ if (healthCheckInterval) {
4074
+ clearInterval(healthCheckInterval);
4075
+ healthCheckInterval = null;
4076
+ }
4077
+ return;
4078
+ }
4166
4079
  if (response.ok || response.status < 500) {
4167
4080
  lastSuccessfulCheck = Date.now();
4168
4081
  } else {
@@ -4371,6 +4284,7 @@ function useMcp(options) {
4371
4284
  headers: headersOption,
4372
4285
  proxyConfig,
4373
4286
  oauthProxyUrl: oauthProxyUrlOption,
4287
+ connectionMode,
4374
4288
  autoProxyFallback = false,
4375
4289
  logLevel: logLevelOption = "silent",
4376
4290
  autoRetry = false,
@@ -4394,6 +4308,7 @@ function useMcp(options) {
4394
4308
  oauth: oauthOptions
4395
4309
  } = options;
4396
4310
  const transportType = "http";
4311
+ const requestedProxyAddress = proxyConfig?.proxyAddress;
4397
4312
  const oauthClientId = oauthOptions?.clientId?.trim() || void 0;
4398
4313
  const oauthClientMetadataUrl = oauthOptions?.clientMetadataUrl?.trim() || void 0;
4399
4314
  const oauthScope = oauthOptions?.scope?.trim() || void 0;
@@ -4441,6 +4356,9 @@ function useMcp(options) {
4441
4356
  );
4442
4357
  const oauthClientConfig = derivedOAuthClientConfig;
4443
4358
  const autoProxyFallbackConfig = useMemo(() => {
4359
+ if (connectionMode === "direct" || connectionMode === "proxy") {
4360
+ return { enabled: false, proxyAddress: void 0 };
4361
+ }
4444
4362
  if (!autoProxyFallback) {
4445
4363
  return { enabled: false, proxyAddress: void 0 };
4446
4364
  }
@@ -4456,7 +4374,7 @@ function useMcp(options) {
4456
4374
  enabled: autoProxyFallback.enabled !== false && Boolean(proxyAddress),
4457
4375
  proxyAddress
4458
4376
  };
4459
- }, [autoProxyFallback, proxyConfig]);
4377
+ }, [autoProxyFallback, connectionMode, proxyConfig]);
4460
4378
  const autoReconnectConfig = useMemo(() => {
4461
4379
  if (autoReconnect === false) {
4462
4380
  return {
@@ -4492,20 +4410,33 @@ function useMcp(options) {
4492
4410
  const [effectiveProxyConfig, setEffectiveProxyConfig] = useState(void 0);
4493
4411
  useEffect(() => {
4494
4412
  setEffectiveProxyConfig(void 0);
4495
- }, [url, proxyConfig]);
4413
+ }, [
4414
+ url,
4415
+ requestedProxyAddress,
4416
+ connectionMode,
4417
+ autoProxyFallbackConfig.proxyAddress
4418
+ ]);
4496
4419
  const activeProxyConfig = useMemo(() => {
4497
- if (!effectiveProxyConfig?.proxyAddress) {
4498
- return proxyConfig;
4420
+ const hasCurrentAutoFallback = autoProxyFallbackConfig.enabled && effectiveProxyConfig?.proxyAddress === autoProxyFallbackConfig.proxyAddress;
4421
+ if (hasCurrentAutoFallback && effectiveProxyConfig) {
4422
+ const latestHeaders = proxyConfig?.headers ?? {};
4423
+ return {
4424
+ ...effectiveProxyConfig,
4425
+ headers: {
4426
+ ...latestHeaders,
4427
+ ...effectiveProxyConfig.headers ?? {}
4428
+ }
4429
+ };
4499
4430
  }
4500
- const latestHeaders = proxyConfig?.headers ?? {};
4501
- return {
4502
- ...effectiveProxyConfig,
4503
- headers: {
4504
- ...latestHeaders,
4505
- ...effectiveProxyConfig.headers ?? {}
4506
- }
4507
- };
4508
- }, [effectiveProxyConfig, proxyConfig]);
4431
+ const startsDirect = connectionMode === "auto" || connectionMode === "direct" || connectionMode === void 0 && autoProxyFallbackConfig.enabled;
4432
+ return startsDirect ? void 0 : proxyConfig;
4433
+ }, [
4434
+ effectiveProxyConfig,
4435
+ proxyConfig,
4436
+ connectionMode,
4437
+ autoProxyFallbackConfig.enabled,
4438
+ autoProxyFallbackConfig.proxyAddress
4439
+ ]);
4509
4440
  const gatewayUrl = activeProxyConfig?.proxyAddress;
4510
4441
  const proxyHeaders = activeProxyConfig?.headers ?? {};
4511
4442
  const effectiveOAuthUrl = useMemo(() => {
@@ -4837,8 +4768,9 @@ function useMcp(options) {
4837
4768
  clientInfo: mergedClientInfo,
4838
4769
  // Pass a fetch that scopes OAuth-proxy routing to this server's
4839
4770
  // transport/auth calls. getProxyFetch wraps `customFetch` (e.g. the
4840
- // OAuth retry fetch for scope step-up) when proxying, or returns it
4841
- // unchanged otherwise. Never mutates the global fetch.
4771
+ // OAuth retry fetch for scope step-up), bypasses the browser cache
4772
+ // for OAuth metadata, and optionally routes OAuth through the BFF.
4773
+ // It never mutates the global fetch.
4842
4774
  ...(() => {
4843
4775
  const scopedFetch = authProviderRef.current?.getProxyFetch?.(customFetch) ?? customFetch;
4844
4776
  return scopedFetch ? { fetch: scopedFetch } : {};
@@ -5039,12 +4971,18 @@ function useMcp(options) {
5039
4971
  if (tokens?.access_token) {
5040
4972
  const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1e3 : void 0;
5041
4973
  let tokenEndpoint = null;
4974
+ let resource = null;
5042
4975
  let clientCreds = null;
5043
4976
  try {
5044
4977
  tokenEndpoint = await authProviderRef.current.getTokenEndpoint?.() ?? null;
5045
4978
  } catch {
5046
4979
  tokenEndpoint = null;
5047
4980
  }
4981
+ try {
4982
+ resource = await authProviderRef.current.getResource?.() ?? null;
4983
+ } catch {
4984
+ resource = null;
4985
+ }
5048
4986
  try {
5049
4987
  clientCreds = await authProviderRef.current.getClientCredentials?.() ?? null;
5050
4988
  } catch {
@@ -5061,6 +4999,7 @@ function useMcp(options) {
5061
4999
  refresh_token: tokens.refresh_token,
5062
5000
  scope: tokens.scope,
5063
5001
  ...tokenEndpoint ? { token_endpoint: tokenEndpoint } : {},
5002
+ ...resource ? { resource } : {},
5064
5003
  ...clientCreds?.client_id ? { client_id: clientCreds.client_id } : {},
5065
5004
  ...clientCreds?.client_secret ? { client_secret: clientCreds.client_secret } : {}
5066
5005
  });
@@ -5305,19 +5244,20 @@ function useMcp(options) {
5305
5244
  addLog("info", "Triggering fresh OAuth authorization...");
5306
5245
  const parsedUrl = new URL(url);
5307
5246
  const baseUrl = parsedUrl.origin + parsedUrl.pathname.replace(/\/+$/, "");
5308
- try {
5309
- await auth2(freshAuthProvider, {
5310
- serverUrl: baseUrl,
5311
- fetchFn: freshAuthProvider.getProxyFetch?.()
5312
- });
5247
+ const authResult = await auth2(freshAuthProvider, {
5248
+ serverUrl: baseUrl,
5249
+ fetchFn: freshAuthProvider.getProxyFetch?.()
5250
+ });
5251
+ if (authResult === "AUTHORIZED") {
5313
5252
  addLog("info", "OAuth flow completed (tokens obtained)");
5314
- } catch (err) {
5315
- addLog(
5316
- "info",
5317
- "OAuth flow initiated (popup/redirect):",
5318
- err instanceof Error ? err.message : "Redirecting..."
5319
- );
5253
+ connectingRef.current = false;
5254
+ connectRef.current?.();
5255
+ return;
5320
5256
  }
5257
+ if (authResult !== "REDIRECT") {
5258
+ throw new Error(`Unexpected OAuth auth() result: ${authResult}`);
5259
+ }
5260
+ addLog("info", "OAuth authorization redirect initiated");
5321
5261
  const newAuthUrl = freshAuthProvider.getLastAttemptedAuthUrl?.();
5322
5262
  if (newAuthUrl) {
5323
5263
  setAuthUrl(newAuthUrl);
@@ -5383,11 +5323,8 @@ function useMcp(options) {
5383
5323
  }
5384
5324
  } catch (authError) {
5385
5325
  if (!isMountedRef.current) return;
5386
- setState("pending_auth");
5387
- addLog(
5388
- "error",
5389
- `Manual authentication failed: ${authError instanceof Error ? authError.message : String(authError)}`
5390
- );
5326
+ const error2 = authError instanceof Error ? authError : new Error(String(authError));
5327
+ failConnection(`Manual authentication failed: ${error2.message}`, error2);
5391
5328
  }
5392
5329
  } else if (currentState === "authenticating") {
5393
5330
  addLog(
@@ -5595,6 +5532,7 @@ function useMcp(options) {
5595
5532
  // Triggers reconnection when proxy fallback changes OAuth URL
5596
5533
  proxyConfig,
5597
5534
  // Triggers reconnection when proxy config (including headers) changes
5535
+ autoProxyFallbackConfig.proxyAddress,
5598
5536
  providedAuthProvider
5599
5537
  ]);
5600
5538
  const retryRef = useRef(retry);
@@ -6711,7 +6649,8 @@ function McpClientProvider({
6711
6649
  }),
6712
6650
  [serverConfigs, defaultCapabilities, defaultServerConfig]
6713
6651
  );
6714
- return /* @__PURE__ */ React.createElement(McpClientContext.Provider, { value: contextValue }, children, mergedServerConfigs.map((config) => /* @__PURE__ */ React.createElement(
6652
+ const skipServerConnections = typeof window !== "undefined" && /\/oauth\/callback\/?$/.test(window.location.pathname);
6653
+ return /* @__PURE__ */ React.createElement(McpClientContext.Provider, { value: contextValue }, children, !skipServerConnections && mergedServerConfigs.map((config) => /* @__PURE__ */ React.createElement(
6715
6654
  McpServerWrapper,
6716
6655
  {
6717
6656
  key: `${config.id}-v${serverRevisions[config.id] ?? 0}`,
@@ -6867,6 +6806,7 @@ import React2, {
6867
6806
  memo,
6868
6807
  useCallback as useCallback6,
6869
6808
  useEffect as useEffect5,
6809
+ useMemo as useMemo3,
6870
6810
  useRef as useRef4,
6871
6811
  useState as useState4
6872
6812
  } from "react";
@@ -6889,6 +6829,53 @@ function parseCustomProps(customProps) {
6889
6829
  return parsed;
6890
6830
  }
6891
6831
 
6832
+ // src/react/view/inject-openai-file-apis.ts
6833
+ var OPENAI_FILE_APIS_SCRIPT = `<script>
6834
+ (function () {
6835
+ var files = new Map();
6836
+ window.openai = window.openai || {};
6837
+ window.openai.uploadFile = async function (file) {
6838
+ var fileId = crypto.randomUUID();
6839
+ files.set(fileId, file);
6840
+ return { fileId: fileId };
6841
+ };
6842
+ window.openai.getFileDownloadUrl = async function (ref) {
6843
+ var file = files.get(ref.fileId);
6844
+ if (!file) {
6845
+ throw new Error("File not found: " + ref.fileId);
6846
+ }
6847
+ return { downloadUrl: URL.createObjectURL(file) };
6848
+ };
6849
+ })();
6850
+ </script>`;
6851
+ function injectOpenAiFileApis(html) {
6852
+ if (html.includes("<head>")) {
6853
+ return html.replace("<head>", "<head>" + OPENAI_FILE_APIS_SCRIPT);
6854
+ }
6855
+ if (html.includes("<HEAD>")) {
6856
+ return html.replace("<HEAD>", "<HEAD>" + OPENAI_FILE_APIS_SCRIPT);
6857
+ }
6858
+ if (html.includes("<html>")) {
6859
+ return html.replace(
6860
+ "<html>",
6861
+ "<html><head>" + OPENAI_FILE_APIS_SCRIPT + "</head>"
6862
+ );
6863
+ }
6864
+ if (html.includes("<HTML>")) {
6865
+ return html.replace(
6866
+ "<HTML>",
6867
+ "<HTML><head>" + OPENAI_FILE_APIS_SCRIPT + "</head>"
6868
+ );
6869
+ }
6870
+ if (html.includes("<!DOCTYPE") || html.includes("<!doctype")) {
6871
+ return html.replace(
6872
+ /(<!DOCTYPE[^>]*>|<!doctype[^>]*>)/i,
6873
+ "$1<head>" + OPENAI_FILE_APIS_SCRIPT + "</head>"
6874
+ );
6875
+ }
6876
+ return OPENAI_FILE_APIS_SCRIPT + html;
6877
+ }
6878
+
6892
6879
  // src/react/view/resolve-view-resource.ts
6893
6880
  function resolveViewResource(options) {
6894
6881
  const { resourceResult, listingResource, cspMode, resourceUri } = options;
@@ -7238,9 +7225,9 @@ function buildSandboxProxyBlobHtml(search) {
7238
7225
  // src/react/view/use-display-mode.ts
7239
7226
  import { useCallback as useCallback5, useEffect as useEffect4 } from "react";
7240
7227
  var SHELL_BASE = "w-full h-full min-h-0 bg-background flex flex-col [&:fullscreen]:h-full [&:fullscreen]:w-full [&:fullscreen]:bg-background";
7241
- var WIDGET_FULLSCREEN_OVERLAY_CLASSES = `fixed inset-0 z-[100] ${SHELL_BASE}`;
7228
+ var WIDGET_FULLSCREEN_OVERLAY_CLASSES = `fixed inset-0 z-[200] ${SHELL_BASE}`;
7242
7229
  var WIDGET_PIP_SHELL_CLASSES = [
7243
- "fixed top-4 left-1/2 -translate-x-1/2 z-[100]",
7230
+ "fixed top-4 left-1/2 -translate-x-1/2 z-[200]",
7244
7231
  "rounded-3xl w-full min-w-[300px] h-[400px]",
7245
7232
  "shadow-2xl border overflow-hidden",
7246
7233
  "bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80",
@@ -7298,7 +7285,8 @@ function useViewDisplayModeControls({
7298
7285
  }
7299
7286
  var VIEW_DIMENSIONS = {
7300
7287
  PIP_MAX_WIDTH: 700,
7301
- DEFAULT_HEIGHT: 400
7288
+ DEFAULT_HEIGHT: 400,
7289
+ FULLSCREEN_HEADER_HEIGHT: 50
7302
7290
  };
7303
7291
 
7304
7292
  // src/react/view/view-detection.ts
@@ -7325,8 +7313,28 @@ var DEFAULT_HOST_CAPABILITIES = {
7325
7313
  serverTools: {},
7326
7314
  serverResources: {},
7327
7315
  logging: {},
7328
- updateModelContext: { text: {} }
7316
+ updateModelContext: { text: {} },
7317
+ // ponytail: always advertised; bridge.onmessage no-ops when onMessage unset
7318
+ message: { text: {} }
7329
7319
  };
7320
+ function CloseIcon() {
7321
+ return /* @__PURE__ */ React2.createElement(
7322
+ "svg",
7323
+ {
7324
+ width: "14",
7325
+ height: "14",
7326
+ viewBox: "0 0 24 24",
7327
+ fill: "none",
7328
+ stroke: "currentColor",
7329
+ strokeWidth: "2",
7330
+ strokeLinecap: "round",
7331
+ strokeLinejoin: "round",
7332
+ "aria-hidden": true
7333
+ },
7334
+ /* @__PURE__ */ React2.createElement("path", { d: "M18 6 6 18" }),
7335
+ /* @__PURE__ */ React2.createElement("path", { d: "m6 6 12 12" })
7336
+ );
7337
+ }
7330
7338
  function waitForSandboxProxyReady(iframe) {
7331
7339
  return new Promise((resolve) => {
7332
7340
  const listener = (event) => {
@@ -7387,6 +7395,10 @@ function ViewRendererBase({
7387
7395
  onResourceResolved,
7388
7396
  wrapTransport,
7389
7397
  toolCallTimeout = DEFAULT_TOOL_CALL_TIMEOUT,
7398
+ mockOpenAiFileApis = false,
7399
+ onInlineHeightChange,
7400
+ fullscreenHeader,
7401
+ renderFullscreenClose,
7390
7402
  className,
7391
7403
  testId = "mcp-app-frame",
7392
7404
  invoking,
@@ -7395,6 +7407,9 @@ function ViewRendererBase({
7395
7407
  const iframeRef = useRef4(null);
7396
7408
  const bridgeRef = useRef4(null);
7397
7409
  const containerRef = useRef4(null);
7410
+ const pendingBlobRevocationsRef = useRef4(
7411
+ /* @__PURE__ */ new Map()
7412
+ );
7398
7413
  const connectionRef = useRef4(
7399
7414
  source.kind === "live" ? source.connection : null
7400
7415
  );
@@ -7408,12 +7423,19 @@ function ViewRendererBase({
7408
7423
  );
7409
7424
  const [internalDisplayMode, setInternalDisplayMode] = useState4("inline");
7410
7425
  const displayMode = displayModeProp ?? internalDisplayMode;
7411
- const hostContextRef = useRef4(hostContext);
7412
- hostContextRef.current = hostContext;
7426
+ const effectiveHostContext = useMemo3(() => {
7427
+ if (!hostContext) return hostContext;
7428
+ if (hostContext.displayMode === displayMode) return hostContext;
7429
+ return { ...hostContext, displayMode };
7430
+ }, [hostContext, displayMode]);
7431
+ const hostContextRef = useRef4(effectiveHostContext);
7432
+ hostContextRef.current = effectiveHostContext;
7413
7433
  const onMessageRef = useRef4(onMessage);
7414
7434
  onMessageRef.current = onMessage;
7415
7435
  const toolInputRef = useRef4(toolInput);
7416
7436
  toolInputRef.current = toolInput;
7437
+ const partialToolInputRef = useRef4(partialToolInput);
7438
+ partialToolInputRef.current = partialToolInput;
7417
7439
  const toolOutputRef = useRef4(toolOutput);
7418
7440
  toolOutputRef.current = toolOutput;
7419
7441
  const customPropsRef = useRef4(customProps);
@@ -7432,10 +7454,14 @@ function ViewRendererBase({
7432
7454
  onReadyRef.current = onReady;
7433
7455
  const onLifecycleChangeRef = useRef4(onLifecycleChange);
7434
7456
  onLifecycleChangeRef.current = onLifecycleChange;
7457
+ const onInlineHeightChangeRef = useRef4(onInlineHeightChange);
7458
+ onInlineHeightChangeRef.current = onInlineHeightChange;
7435
7459
  const sandboxUrlRef = useRef4(sandboxUrl);
7436
7460
  sandboxUrlRef.current = sandboxUrl;
7437
7461
  const cspModeRef = useRef4(cspMode);
7438
7462
  cspModeRef.current = cspMode;
7463
+ const mockOpenAiFileApisRef = useRef4(mockOpenAiFileApis);
7464
+ mockOpenAiFileApisRef.current = mockOpenAiFileApis;
7439
7465
  const resolveSandboxUrl = useCallback6((next) => {
7440
7466
  const custom = sandboxUrlRef.current;
7441
7467
  if (custom) {
@@ -7539,10 +7565,18 @@ function ViewRendererBase({
7539
7565
  }, [source.kind, liveResourceUri, preloadedHtml, cspMode, resolveSandboxUrl]);
7540
7566
  useEffect5(() => {
7541
7567
  const url = activeSandboxUrl;
7568
+ if (!url || url.protocol !== "blob:") return;
7569
+ const pending = pendingBlobRevocationsRef.current.get(url.href);
7570
+ if (pending) {
7571
+ clearTimeout(pending);
7572
+ pendingBlobRevocationsRef.current.delete(url.href);
7573
+ }
7542
7574
  return () => {
7543
- if (url?.protocol === "blob:") {
7575
+ const timer = setTimeout(() => {
7544
7576
  URL.revokeObjectURL(url.href);
7545
- }
7577
+ pendingBlobRevocationsRef.current.delete(url.href);
7578
+ }, 1e3);
7579
+ pendingBlobRevocationsRef.current.set(url.href, timer);
7546
7580
  };
7547
7581
  }, [activeSandboxUrl]);
7548
7582
  const isBlobSandbox = activeSandboxUrl?.protocol === "blob:";
@@ -7603,7 +7637,14 @@ function ViewRendererBase({
7603
7637
  iframe.setAttribute("allow", allowAttribute);
7604
7638
  }
7605
7639
  const readyPromise = waitForSandboxProxyReady(iframe);
7606
- iframe.src = activeSandboxUrl.href;
7640
+ if (activeSandboxUrl.protocol === "blob:") {
7641
+ const response = await fetch(activeSandboxUrl.href);
7642
+ const sandboxHtml = await response.text();
7643
+ if (disposed) return;
7644
+ iframe.srcdoc = sandboxHtml;
7645
+ } else {
7646
+ iframe.src = activeSandboxUrl.href;
7647
+ }
7607
7648
  await readyPromise;
7608
7649
  if (disposed) return;
7609
7650
  const capabilities = {
@@ -7689,7 +7730,10 @@ function ViewRendererBase({
7689
7730
  height
7690
7731
  }) => {
7691
7732
  if (displayModeRef.current !== "inline") return;
7692
- if (height !== void 0) setInlineHeight(height);
7733
+ if (height !== void 0) {
7734
+ setInlineHeight(height);
7735
+ onInlineHeightChangeRef.current?.(height);
7736
+ }
7693
7737
  };
7694
7738
  const initPromise = hookInitialized(bridge);
7695
7739
  let transport = new PostMessageTransport(
@@ -7702,7 +7746,7 @@ function ViewRendererBase({
7702
7746
  await bridge.connect(transport);
7703
7747
  if (disposed) return;
7704
7748
  await bridge.sendSandboxResourceReady({
7705
- html: resolved.html,
7749
+ html: mockOpenAiFileApisRef.current ? injectOpenAiFileApis(resolved.html) : resolved.html,
7706
7750
  csp: resolved.csp,
7707
7751
  permissions: resolved.permissions
7708
7752
  });
@@ -7711,11 +7755,18 @@ function ViewRendererBase({
7711
7755
  bridgeRef.current = bridge;
7712
7756
  setInitCount((c) => c + 1);
7713
7757
  onLifecycleChangeRef.current?.({ status: "initialized" });
7714
- const mergedArgs = {
7715
- ...toolInputRef.current,
7716
- ...parseCustomProps(customPropsRef.current)
7717
- };
7718
- bridge.sendToolInput({ arguments: mergedArgs });
7758
+ const currentPartialToolInput = partialToolInputRef.current;
7759
+ if (currentPartialToolInput) {
7760
+ bridge.sendToolInputPartial({
7761
+ arguments: currentPartialToolInput
7762
+ });
7763
+ } else {
7764
+ const mergedArgs = {
7765
+ ...toolInputRef.current,
7766
+ ...parseCustomProps(customPropsRef.current)
7767
+ };
7768
+ bridge.sendToolInput({ arguments: mergedArgs });
7769
+ }
7719
7770
  const toolResultPayload = buildToolResultPayload(
7720
7771
  toolOutputRef.current,
7721
7772
  customPropsRef.current
@@ -7764,13 +7815,14 @@ function ViewRendererBase({
7764
7815
  cspMode,
7765
7816
  viewId,
7766
7817
  wrapTransport,
7767
- toolCallTimeout
7818
+ toolCallTimeout,
7819
+ mockOpenAiFileApis
7768
7820
  ]);
7769
7821
  useEffect5(() => {
7770
7822
  const bridge = bridgeRef.current;
7771
- if (!bridge || initCount === 0 || !hostContext) return;
7772
- bridge.setHostContext(hostContext);
7773
- }, [hostContext, initCount]);
7823
+ if (!bridge || initCount === 0 || !effectiveHostContext) return;
7824
+ bridge.setHostContext(effectiveHostContext);
7825
+ }, [effectiveHostContext, initCount]);
7774
7826
  useEffect5(() => {
7775
7827
  const bridge = bridgeRef.current;
7776
7828
  if (!bridge || initCount === 0 || !partialToolInput) return;
@@ -7778,13 +7830,13 @@ function ViewRendererBase({
7778
7830
  }, [initCount, partialToolInput]);
7779
7831
  useEffect5(() => {
7780
7832
  const bridge = bridgeRef.current;
7781
- if (!bridge || initCount === 0) return;
7833
+ if (!bridge || initCount === 0 || partialToolInput) return;
7782
7834
  const mergedArgs = {
7783
7835
  ...toolInput,
7784
7836
  ...parseCustomProps(customProps)
7785
7837
  };
7786
7838
  bridge.sendToolInput({ arguments: mergedArgs });
7787
- }, [initCount, toolInput, customProps]);
7839
+ }, [initCount, toolInput, partialToolInput, customProps]);
7788
7840
  useEffect5(() => {
7789
7841
  const bridge = bridgeRef.current;
7790
7842
  if (!bridge || initCount === 0) return;
@@ -7808,6 +7860,7 @@ function ViewRendererBase({
7808
7860
  const timer = setTimeout(() => setShowSpinner(false), 300);
7809
7861
  return () => clearTimeout(timer);
7810
7862
  }, [initCount, showSpinner]);
7863
+ const showHostBorder = resolved !== null && resolved.prefersBorder && displayMode !== "fullscreen";
7811
7864
  if (loadError) {
7812
7865
  return /* @__PURE__ */ React2.createElement("div", { className }, /* @__PURE__ */ React2.createElement("div", { className: "border border-red-200/50 dark:border-red-800/50 bg-red-50/30 dark:bg-red-950/20 rounded-lg p-4" }, /* @__PURE__ */ React2.createElement("p", { className: "text-sm text-red-600 dark:text-red-400" }, "Failed to load view: ", loadError)));
7813
7866
  }
@@ -7825,29 +7878,69 @@ function ViewRendererBase({
7825
7878
  "div",
7826
7879
  {
7827
7880
  ref: containerRef,
7828
- className: containerClassName,
7881
+ className: isFullscreen ? `${containerClassName} flex flex-col` : containerClassName,
7829
7882
  style: isPip ? {
7830
7883
  height: VIEW_DIMENSIONS.DEFAULT_HEIGHT,
7831
7884
  maxWidth: VIEW_DIMENSIONS.PIP_MAX_WIDTH,
7832
7885
  zIndex: 100
7833
7886
  } : isFullscreen ? { zIndex: 100 } : void 0
7834
7887
  },
7835
- (isFullscreen || isPip) && /* @__PURE__ */ React2.createElement(
7888
+ isFullscreen && // ponytail: inspector Tailwind may not emit client arbitrary classes
7889
+ // (h-[50px], grid-cols-[auto_1fr_auto]) — use inline layout instead.
7890
+ /* @__PURE__ */ React2.createElement(
7891
+ "header",
7892
+ {
7893
+ className: "grid shrink-0 items-center border-b border-zinc-200 bg-background px-3 dark:border-zinc-700",
7894
+ style: {
7895
+ height: VIEW_DIMENSIONS.FULLSCREEN_HEADER_HEIGHT,
7896
+ gridTemplateColumns: "auto 1fr auto"
7897
+ }
7898
+ },
7899
+ renderFullscreenClose ? renderFullscreenClose({
7900
+ onClick: () => void handleDisplayModeChange("inline"),
7901
+ "data-testid": "debugger-exit-fullscreen-button",
7902
+ "aria-label": "Exit fullscreen"
7903
+ }) : /* @__PURE__ */ React2.createElement(
7904
+ "button",
7905
+ {
7906
+ type: "button",
7907
+ "data-testid": "debugger-exit-fullscreen-button",
7908
+ "aria-label": "Exit fullscreen",
7909
+ className: "flex size-8 cursor-pointer items-center justify-center rounded-full border border-zinc-200 bg-background text-foreground shadow-sm hover:bg-muted dark:border-zinc-700",
7910
+ onClick: () => void handleDisplayModeChange("inline")
7911
+ },
7912
+ /* @__PURE__ */ React2.createElement(CloseIcon, null)
7913
+ ),
7914
+ /* @__PURE__ */ React2.createElement("div", { className: "flex min-w-0 items-center justify-center gap-2 px-2" }, fullscreenHeader?.iconUrl ? /* @__PURE__ */ React2.createElement(
7915
+ "img",
7916
+ {
7917
+ src: fullscreenHeader.iconUrl,
7918
+ alt: "",
7919
+ className: "size-6 shrink-0 rounded-md object-contain"
7920
+ }
7921
+ ) : null, /* @__PURE__ */ React2.createElement("span", { className: "truncate text-sm font-medium text-foreground" }, fullscreenHeader?.title ?? toolName)),
7922
+ /* @__PURE__ */ React2.createElement("div", { className: "size-8 shrink-0", "aria-hidden": true })
7923
+ ),
7924
+ isPip && (renderFullscreenClose ? /* @__PURE__ */ React2.createElement("div", { className: "absolute right-3 top-3", style: { zIndex: 110 } }, renderFullscreenClose({
7925
+ onClick: () => void handleDisplayModeChange("inline"),
7926
+ "data-testid": "debugger-exit-pip-button",
7927
+ "aria-label": "Exit picture-in-picture"
7928
+ })) : /* @__PURE__ */ React2.createElement(
7836
7929
  "button",
7837
7930
  {
7838
7931
  type: "button",
7839
- "data-testid": isFullscreen ? "debugger-exit-fullscreen-button" : "debugger-exit-pip-button",
7840
- "aria-label": isFullscreen ? "Exit fullscreen" : "Exit picture-in-picture",
7841
- className: "absolute right-3 top-3 z-[110] flex size-8 items-center justify-center rounded-full border border-border bg-background/90 text-lg leading-none text-foreground shadow-sm backdrop-blur-sm hover:bg-background",
7932
+ "data-testid": "debugger-exit-pip-button",
7933
+ "aria-label": "Exit picture-in-picture",
7934
+ className: "absolute right-3 top-3 z-[110] flex size-8 cursor-pointer items-center justify-center rounded-full border border-border bg-background/90 text-foreground shadow-sm backdrop-blur-sm hover:bg-background",
7842
7935
  style: { zIndex: 110 },
7843
7936
  onClick: () => void handleDisplayModeChange("inline")
7844
7937
  },
7845
- "\xD7"
7846
- ),
7938
+ /* @__PURE__ */ React2.createElement(CloseIcon, null)
7939
+ )),
7847
7940
  /* @__PURE__ */ React2.createElement(
7848
7941
  "div",
7849
7942
  {
7850
- className: isFullscreen || isPip ? "relative w-full h-full min-h-0 flex flex-1 flex-col" : "relative w-full flex flex-1 justify-center items-center"
7943
+ className: isFullscreen ? "relative flex min-h-0 w-full flex-1 flex-col" : isPip ? "relative w-full h-full min-h-0 flex flex-1 flex-col" : "relative w-full flex flex-1 justify-center items-center"
7851
7944
  },
7852
7945
  showSpinner && /* @__PURE__ */ React2.createElement("div", { className: "flex absolute inset-0 items-center justify-center z-10" }, /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-muted-foreground" }, "Loading\u2026")),
7853
7946
  !isPip && !isFullscreen && (invoking || invoked) && /* @__PURE__ */ React2.createElement("div", { className: "absolute -top-8 left-2 z-10 whitespace-nowrap pointer-events-none text-xs text-muted-foreground" }, invoking && !toolOutput ? invoking : invoked),
@@ -7864,10 +7957,7 @@ function ViewRendererBase({
7864
7957
  {
7865
7958
  ref: iframeRef,
7866
7959
  title: `MCP App: ${toolName}`,
7867
- className: "w-full h-full border-0 bg-transparent",
7868
- style: {
7869
- border: resolved.prefersBorder && displayMode !== "fullscreen" ? void 0 : "none"
7870
- }
7960
+ className: showHostBorder ? "w-full h-full bg-transparent border border-border rounded-xl" : "w-full h-full bg-transparent border-0"
7871
7961
  }
7872
7962
  )
7873
7963
  )
@@ -7888,6 +7978,10 @@ function viewRendererAreEqual(prev, next) {
7888
7978
  if (prev.hostContext !== next.hostContext) return false;
7889
7979
  if (prev.hostCapabilities !== next.hostCapabilities) return false;
7890
7980
  if (prev.cspMode !== next.cspMode) return false;
7981
+ if (prev.mockOpenAiFileApis !== next.mockOpenAiFileApis) return false;
7982
+ if (prev.onInlineHeightChange !== next.onInlineHeightChange) return false;
7983
+ if (prev.fullscreenHeader !== next.fullscreenHeader) return false;
7984
+ if (prev.renderFullscreenClose !== next.renderFullscreenClose) return false;
7891
7985
  if (prev.className !== next.className) return false;
7892
7986
  if (prev.onReady !== next.onReady) return false;
7893
7987
  if (prev.onLifecycleChange !== next.onLifecycleChange) return false;