@mcp-use/client 2.0.0-beta.1 → 2.0.0-beta.11

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 (37) 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 +6 -1
  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 +139 -166
  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 +65 -170
  13. package/dist/index.js.map +1 -1
  14. package/dist/react/index.js +406 -233
  15. package/dist/react/index.js.map +1 -1
  16. package/dist/react/types.d.ts +9 -3
  17. package/dist/react/types.d.ts.map +1 -1
  18. package/dist/react/useMcp-helpers.d.ts.map +1 -1
  19. package/dist/react/useMcp-operations.d.ts.map +1 -1
  20. package/dist/react/useMcp.d.ts.map +1 -1
  21. package/dist/react/view/ViewRenderer.d.ts +2 -1
  22. package/dist/react/view/ViewRenderer.d.ts.map +1 -1
  23. package/dist/react/view/inject-openai-file-apis.d.ts +6 -0
  24. package/dist/react/view/inject-openai-file-apis.d.ts.map +1 -0
  25. package/dist/react/view/types.d.ts +16 -0
  26. package/dist/react/view/types.d.ts.map +1 -1
  27. package/dist/react/view/use-display-mode.d.ts +1 -0
  28. package/dist/react/view/use-display-mode.d.ts.map +1 -1
  29. package/dist/telemetry/index.d.ts +1 -1
  30. package/dist/telemetry/index.d.ts.map +1 -1
  31. package/dist/telemetry/tel-fetch.d.ts +0 -6
  32. package/dist/telemetry/tel-fetch.d.ts.map +1 -1
  33. package/dist/telemetry/telemetry-node.d.ts.map +1 -1
  34. package/dist/telemetry/telemetry.d.ts +0 -8
  35. package/dist/telemetry/telemetry.d.ts.map +1 -1
  36. package/dist/transport/http.d.ts +6 -6
  37. 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.10";
1477
1477
  function getPackageVersion() {
1478
1478
  return VERSION;
1479
1479
  }
@@ -1696,29 +1696,39 @@ var OAuthSessionStore = class _OAuthSessionStore {
1696
1696
  if (ctx) await this.store.set(this.credentialKey("tokens"), serialized);
1697
1697
  await this.store.remove(this.getKey("code_verifier"));
1698
1698
  await this.store.remove(this.getKey("last_auth_url"));
1699
+ await this.store.remove(this.getKey("last_auth_callback_url"));
1699
1700
  }
1700
1701
  async clientInformation(ctx) {
1702
+ if (!this.allowClientSecret) {
1703
+ const registeredRedirectUri = await this.store.get(
1704
+ this.getKey("client_info_redirect_uri")
1705
+ );
1706
+ if (registeredRedirectUri !== this.redirectUrl) {
1707
+ await this.invalidateCredentials("registration");
1708
+ console.info(
1709
+ `[${this.storageKeyPrefix}] Re-registering browser OAuth client after its Inspector callback changed or could not be verified.`
1710
+ );
1711
+ return void 0;
1712
+ }
1713
+ }
1701
1714
  const stored = await this.readCredential("client_info", ctx);
1702
1715
  if (!stored) return void 0;
1703
1716
  const { key, value: clientInfo } = stored;
1704
1717
  try {
1705
1718
  if (!this.allowClientSecret && clientInfo.client_secret) {
1706
- await this.store.remove(key);
1707
- if (ctx) await this.store.remove(this.credentialKey("client_info"));
1719
+ await this.invalidateCredentials("registration");
1708
1720
  console.warn(
1709
- `[${this.storageKeyPrefix}] Removed OAuth client information containing a browser client_secret.`
1721
+ `[${this.storageKeyPrefix}] Recovered stale browser OAuth credentials containing a client_secret.`
1710
1722
  );
1711
1723
  return void 0;
1712
1724
  }
1713
1725
  const storedRedirectUris = Array.isArray(clientInfo.redirect_uris) ? clientInfo.redirect_uris : [];
1714
- const hasMatchingRedirect = storedRedirectUris.length === 0 || storedRedirectUris.includes(this.redirectUrl);
1726
+ const hasMatchingRedirect = storedRedirectUris.length === 0 && this.allowClientSecret || storedRedirectUris.includes(this.redirectUrl);
1715
1727
  if (!hasMatchingRedirect) {
1716
1728
  console.info(
1717
- `[${this.storageKeyPrefix}] Invalidating cached OAuth client info due to redirect URI mismatch.`
1729
+ `[${this.storageKeyPrefix}] Recovering cached OAuth credentials after a redirect URI change.`
1718
1730
  );
1719
- await this.store.remove(key);
1720
- await this.store.remove(this.credentialKey("tokens", ctx));
1721
- await this.store.remove(this.getKey("last_auth_url"));
1731
+ await this.invalidateCredentials("registration");
1722
1732
  return void 0;
1723
1733
  }
1724
1734
  return clientInfo;
@@ -1736,11 +1746,20 @@ var OAuthSessionStore = class _OAuthSessionStore {
1736
1746
  "Browser OAuth clients must be public clients; client_secret persistence is not allowed."
1737
1747
  );
1738
1748
  }
1739
- const serialized = JSON.stringify(clientInformation);
1749
+ const persistedClientInformation = !this.allowClientSecret && (!("redirect_uris" in clientInformation) || !Array.isArray(
1750
+ clientInformation.redirect_uris
1751
+ ) || clientInformation.redirect_uris.length === 0) ? { ...clientInformation, redirect_uris: [this.redirectUrl] } : clientInformation;
1752
+ const serialized = JSON.stringify(persistedClientInformation);
1740
1753
  await this.store.set(this.credentialKey("client_info", ctx), serialized);
1741
1754
  if (ctx) {
1742
1755
  await this.store.set(this.credentialKey("client_info"), serialized);
1743
1756
  }
1757
+ if (!this.allowClientSecret) {
1758
+ await this.store.set(
1759
+ this.getKey("client_info_redirect_uri"),
1760
+ this.redirectUrl
1761
+ );
1762
+ }
1744
1763
  }
1745
1764
  async saveCodeVerifier(codeVerifier) {
1746
1765
  await this.store.set(this.getKey("code_verifier"), codeVerifier);
@@ -1765,11 +1784,22 @@ var OAuthSessionStore = class _OAuthSessionStore {
1765
1784
  }
1766
1785
  };
1767
1786
  switch (scope) {
1787
+ case "registration":
1788
+ await removeCredentialKeys("tokens");
1789
+ await removeCredentialKeys("client_info");
1790
+ await this.store.remove(this.getKey("code_verifier"));
1791
+ await this.store.remove(this.getKey("last_auth_url"));
1792
+ await this.store.remove(this.getKey("last_auth_callback_url"));
1793
+ await this.store.remove(this.getKey("client_info_redirect_uri"));
1794
+ await this.store.remove(this.getKey("token_endpoint"));
1795
+ break;
1768
1796
  case "all":
1769
1797
  await removeCredentialKeys("tokens");
1770
1798
  await removeCredentialKeys("client_info");
1771
1799
  await this.store.remove(this.getKey("code_verifier"));
1772
1800
  await this.store.remove(this.getKey("last_auth_url"));
1801
+ await this.store.remove(this.getKey("last_auth_callback_url"));
1802
+ await this.store.remove(this.getKey("client_info_redirect_uri"));
1773
1803
  await this.store.remove(this.getKey("discovery_state"));
1774
1804
  await this.store.remove(this.getKey("token_endpoint"));
1775
1805
  break;
@@ -1839,6 +1869,10 @@ var OAuthSessionStore = class _OAuthSessionStore {
1839
1869
  authorizationUrl.searchParams.set("state", state);
1840
1870
  const sanitizedAuthUrl = sanitizeUrl(authorizationUrl.toString());
1841
1871
  await this.store.set(stateKey, JSON.stringify(stateData));
1872
+ await this.store.set(
1873
+ this.getKey("last_auth_callback_url"),
1874
+ this.redirectUrl
1875
+ );
1842
1876
  await this.store.set(this.getKey("last_auth_url"), sanitizedAuthUrl);
1843
1877
  return sanitizedAuthUrl;
1844
1878
  }
@@ -1849,6 +1883,14 @@ var OAuthSessionStore = class _OAuthSessionStore {
1849
1883
  async getTokenEndpoint() {
1850
1884
  return (await this.discoveryState())?.authorizationServerMetadata?.token_endpoint ?? null;
1851
1885
  }
1886
+ /**
1887
+ * Return the protected-resource URL selected during OAuth discovery.
1888
+ * Consumers can persist it and reuse it for server-side refresh exchanges.
1889
+ */
1890
+ async getResource() {
1891
+ const resource = (await this.discoveryState())?.resourceMetadata?.resource;
1892
+ return typeof resource === "string" ? resource : null;
1893
+ }
1852
1894
  };
1853
1895
 
1854
1896
  // src/auth/browser.ts
@@ -1966,20 +2008,19 @@ var BrowserOAuthClientProvider = class {
1966
2008
  * therefore never alters fetch behavior for other servers, other
1967
2009
  * connections, or the rest of the page.
1968
2010
  *
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`).
2011
+ * OAuth metadata is always fetched with `cache: "no-store"`, including in
2012
+ * direct mode. Authorization servers commonly vary CORS headers by Origin;
2013
+ * bypassing the browser HTTP cache prevents a revalidated response cached
2014
+ * for another localhost origin from poisoning discovery. When OAuth proxying
2015
+ * is disabled or no `oauthProxyUrl` is configured, all requests still go
2016
+ * directly to their original URLs.
1973
2017
  *
1974
2018
  * @param baseFetch - The fetch used for non-OAuth requests and for the
1975
2019
  * underlying proxy calls. Defaults to the global `fetch`.
1976
2020
  */
1977
2021
  getProxyFetch(baseFetch) {
1978
- if (!this.proxyOAuthRequests || !this.oauthProxyUrl) {
1979
- return baseFetch;
1980
- }
1981
2022
  const base = baseFetch ?? globalThis.fetch.bind(globalThis);
1982
- const oauthProxyUrl = this.oauthProxyUrl;
2023
+ const oauthProxyUrl = this.proxyOAuthRequests && this.oauthProxyUrl ? this.oauthProxyUrl : void 0;
1983
2024
  const discoveredEndpoints = /* @__PURE__ */ new Set();
1984
2025
  let restoredDiscovery = false;
1985
2026
  return async (input, init) => {
@@ -1992,6 +2033,12 @@ var BrowserOAuthClientProvider = class {
1992
2033
  return await base(input, init);
1993
2034
  }
1994
2035
  const isMetadata = pathname.includes("/.well-known/");
2036
+ if (!oauthProxyUrl) {
2037
+ return await base(
2038
+ isMetadata ? url : input,
2039
+ isMetadata ? { ...init, cache: "no-store" } : init
2040
+ );
2041
+ }
1995
2042
  if (!restoredDiscovery) {
1996
2043
  restoredDiscovery = true;
1997
2044
  const metadata = (await this.discoveryState())?.authorizationServerMetadata;
@@ -2026,7 +2073,8 @@ var BrowserOAuthClientProvider = class {
2026
2073
  if (isMetadata) {
2027
2074
  const response2 = await base(proxyEndpoint, {
2028
2075
  ...init,
2029
- method: "GET"
2076
+ method: "GET",
2077
+ cache: "no-store"
2030
2078
  });
2031
2079
  try {
2032
2080
  const metadata = await response2.clone().json();
@@ -2044,23 +2092,40 @@ var BrowserOAuthClientProvider = class {
2044
2092
  }
2045
2093
  return response2;
2046
2094
  }
2047
- const body = init?.body ? await serializeBody(init.body) : void 0;
2095
+ const inputRequest = input instanceof Request ? input : void 0;
2096
+ const method = init?.method ?? inputRequest?.method ?? "POST";
2097
+ const requestHeaders = init?.headers ?? inputRequest?.headers;
2098
+ let body;
2099
+ if (init?.body !== void 0 && init.body !== null) {
2100
+ body = await serializeBody(init.body);
2101
+ } else if (inputRequest?.body && method !== "GET" && method !== "HEAD") {
2102
+ body = await inputRequest.clone().text();
2103
+ }
2048
2104
  const response = await base(proxyEndpoint, {
2049
2105
  method: "POST",
2050
2106
  headers: { "Content-Type": "application/json" },
2051
2107
  body: JSON.stringify({
2052
2108
  serverUrl: this.serverUrl,
2053
2109
  url,
2054
- method: init?.method || "POST",
2055
- headers: init?.headers ? Object.fromEntries(new Headers(init.headers)) : {},
2110
+ method,
2111
+ headers: requestHeaders ? Object.fromEntries(new Headers(requestHeaders)) : {},
2056
2112
  body
2057
2113
  })
2058
2114
  });
2059
2115
  const data = await response.json();
2116
+ if (!response.ok || typeof data.status !== "number") {
2117
+ return new Response(JSON.stringify(data), {
2118
+ status: response.status,
2119
+ statusText: response.statusText,
2120
+ headers: response.headers
2121
+ });
2122
+ }
2060
2123
  return new Response(JSON.stringify(data.body), {
2061
2124
  status: data.status,
2062
- statusText: data.statusText,
2063
- headers: new Headers(data.headers)
2125
+ statusText: typeof data.statusText === "string" ? data.statusText : void 0,
2126
+ headers: new Headers(
2127
+ data.headers && typeof data.headers === "object" ? data.headers : void 0
2128
+ )
2064
2129
  });
2065
2130
  };
2066
2131
  }
@@ -2083,7 +2148,16 @@ var BrowserOAuthClientProvider = class {
2083
2148
  }
2084
2149
  async saveClientInformation(clientInformation, ctx) {
2085
2150
  if (this.staticClientInfo) return;
2086
- return this.session.saveClientInformation(clientInformation, ctx);
2151
+ const { client_secret: discardedClientSecret, ...publicClientInformation } = clientInformation;
2152
+ if (discardedClientSecret) {
2153
+ console.info(
2154
+ `[${this.storageKeyPrefix}] Discarded client_secret returned for a public browser OAuth client.`
2155
+ );
2156
+ }
2157
+ return this.session.saveClientInformation(
2158
+ publicClientInformation,
2159
+ ctx
2160
+ );
2087
2161
  }
2088
2162
  codeVerifier() {
2089
2163
  return this.session.codeVerifier();
@@ -2113,6 +2187,10 @@ var BrowserOAuthClientProvider = class {
2113
2187
  getTokenEndpoint() {
2114
2188
  return this.session.getTokenEndpoint();
2115
2189
  }
2190
+ /** Return the protected-resource URL selected during OAuth discovery. */
2191
+ getResource() {
2192
+ return this.session.getResource();
2193
+ }
2116
2194
  /**
2117
2195
  * Return the stored public OAuth client ID. Browser providers do not retain
2118
2196
  * client secrets.
@@ -2191,7 +2269,31 @@ var BrowserOAuthClientProvider = class {
2191
2269
  getLastAttemptedAuthUrl() {
2192
2270
  const storedUrl = localStorage.getItem(this.getKey("last_auth_url"));
2193
2271
  if (!storedUrl) return null;
2194
- return sanitizeUrl(storedUrl);
2272
+ const storedCallbackUrl = localStorage.getItem(
2273
+ this.getKey("last_auth_callback_url")
2274
+ );
2275
+ if (storedCallbackUrl !== this.callbackUrl) {
2276
+ console.info(
2277
+ `[${this.storageKeyPrefix}] Recovering stale OAuth state whose callback cannot be verified.`
2278
+ );
2279
+ this.clearStorage();
2280
+ return null;
2281
+ }
2282
+ const sanitized = sanitizeUrl(storedUrl);
2283
+ try {
2284
+ const redirectUri = new URL(sanitized).searchParams.get("redirect_uri");
2285
+ if (redirectUri && new URL(redirectUri).toString() !== new URL(this.callbackUrl).toString()) {
2286
+ console.info(
2287
+ `[${this.storageKeyPrefix}] Recovering stale OAuth state after the Inspector callback path changed.`
2288
+ );
2289
+ this.clearStorage();
2290
+ return null;
2291
+ }
2292
+ } catch {
2293
+ this.clearStorage();
2294
+ return null;
2295
+ }
2296
+ return sanitized;
2195
2297
  }
2196
2298
  clearStorage() {
2197
2299
  const prefixPattern = `${this.storageKeyPrefix}_${this.serverUrlHash}_`;
@@ -2370,62 +2472,6 @@ async function telFetch(url, init) {
2370
2472
  }
2371
2473
  var POSTHOG_HOST = "https://eu.i.posthog.com";
2372
2474
  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
2475
  function capturePostHog(params) {
2430
2476
  const host = params.host ?? POSTHOG_HOST;
2431
2477
  const apiKey = params.apiKey ?? POSTHOG_API_KEY;
@@ -2442,13 +2488,6 @@ function capturePostHog(params) {
2442
2488
  })
2443
2489
  });
2444
2490
  }
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
2491
 
2453
2492
  // src/telemetry/telemetry.ts
2454
2493
  function generateUUID() {
@@ -2459,18 +2498,6 @@ function secureRandomString() {
2459
2498
  globalThis.crypto.getRandomValues(array);
2460
2499
  return Array.from(array, (v) => v.toString(16).padStart(2, "0")).join("");
2461
2500
  }
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
2501
  var USER_ID_STORAGE_KEY = "mcp_use_user_id";
2475
2502
  var PROJECT_API_KEY = "phc_lyTtbYwvkdSbrcMQNPiKiiRWrrM1seyKIMjycSvItEI";
2476
2503
  var HOST = "https://eu.i.posthog.com";
@@ -2499,10 +2526,7 @@ function createLocalStorageBackend() {
2499
2526
  localStorage.setItem(USER_ID_STORAGE_KEY, id);
2500
2527
  } catch {
2501
2528
  }
2502
- },
2503
- // Package-download dedup is node/Scarf-oriented; localStorage unused.
2504
- getDownloadedVersion: () => null,
2505
- setDownloadedVersion: () => void 0
2529
+ }
2506
2530
  };
2507
2531
  }
2508
2532
  function detectRuntimeEnvironment() {
@@ -2570,17 +2594,13 @@ var Telemetry = class _Telemetry {
2570
2594
  _currUserId = null;
2571
2595
  _telemetryEnabled = false;
2572
2596
  _pending = /* @__PURE__ */ new Set();
2573
- _scarfEnabled = false;
2574
2597
  _runtimeEnvironment;
2575
2598
  _storageCapability;
2576
2599
  _storage;
2577
- /** True when node entry installed fs storage (package-download dedup). */
2578
- _fsBacked;
2579
2600
  _source;
2580
2601
  _productVersion;
2581
2602
  constructor() {
2582
2603
  this._runtimeEnvironment = detectRuntimeEnvironment();
2583
- this._fsBacked = configuredStorage !== null;
2584
2604
  this._storage = configuredStorage ?? createLocalStorageBackend() ?? null;
2585
2605
  this._storageCapability = this._storage ? "persistent" : "session-only";
2586
2606
  this._source = readSourceHint() || this._runtimeEnvironment;
@@ -2599,14 +2619,6 @@ var Telemetry = class _Telemetry {
2599
2619
  "Anonymized telemetry enabled. Set MCP_USE_ANONYMIZED_TELEMETRY=false to disable."
2600
2620
  );
2601
2621
  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
2622
  }
2611
2623
  }
2612
2624
  get runtimeEnvironment() {
@@ -2638,7 +2650,7 @@ var Telemetry = class _Telemetry {
2638
2650
  this._productVersion = version;
2639
2651
  }
2640
2652
  get isEnabled() {
2641
- return this._telemetryEnabled || this._scarfEnabled;
2653
+ return this._telemetryEnabled;
2642
2654
  }
2643
2655
  get userId() {
2644
2656
  if (this._currUserId) return this._currUserId;
@@ -2661,7 +2673,7 @@ var Telemetry = class _Telemetry {
2661
2673
  return this._currUserId;
2662
2674
  }
2663
2675
  async capture(event) {
2664
- if (!this._telemetryEnabled && !this._scarfEnabled) return;
2676
+ if (!this._telemetryEnabled) return;
2665
2677
  const currentUserId = this.userId;
2666
2678
  const properties = {
2667
2679
  ...event.properties,
@@ -2670,52 +2682,15 @@ var Telemetry = class _Telemetry {
2670
2682
  source: this._source,
2671
2683
  runtime: this._runtimeEnvironment
2672
2684
  };
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
2685
+ const p = capturePostHog({
2686
+ host: HOST,
2687
+ apiKey: PROJECT_API_KEY,
2688
+ event: event.name,
2689
+ distinctId: currentUserId,
2690
+ properties
2718
2691
  });
2692
+ this._pending.add(p);
2693
+ void p.finally(() => this._pending.delete(p));
2719
2694
  }
2720
2695
  async trackAgentExecution(data) {
2721
2696
  if (!this.isEnabled) return;
@@ -4162,9 +4137,14 @@ function startConnectionHealthMonitoring(params) {
4162
4137
  return;
4163
4138
  }
4164
4139
  const authHeaders = params.getAuthHeaders ? await params.getAuthHeaders() : {};
4140
+ const healthCheckHeaders = {
4141
+ ...params.allHeaders,
4142
+ ...authHeaders,
4143
+ ...params.gatewayUrl && params.url ? { "X-Target-URL": params.url } : {}
4144
+ };
4165
4145
  const response = await fetch(healthCheckUrl, {
4166
4146
  method: "HEAD",
4167
- headers: { ...params.allHeaders, ...authHeaders },
4147
+ headers: healthCheckHeaders,
4168
4148
  signal: AbortSignal.timeout(5e3)
4169
4149
  });
4170
4150
  if (response.status === 405 || response.status === 404) {
@@ -4384,6 +4364,7 @@ function useMcp(options) {
4384
4364
  headers: headersOption,
4385
4365
  proxyConfig,
4386
4366
  oauthProxyUrl: oauthProxyUrlOption,
4367
+ connectionMode,
4387
4368
  autoProxyFallback = false,
4388
4369
  logLevel: logLevelOption = "silent",
4389
4370
  autoRetry = false,
@@ -4407,6 +4388,7 @@ function useMcp(options) {
4407
4388
  oauth: oauthOptions
4408
4389
  } = options;
4409
4390
  const transportType = "http";
4391
+ const requestedProxyAddress = proxyConfig?.proxyAddress;
4410
4392
  const oauthClientId = oauthOptions?.clientId?.trim() || void 0;
4411
4393
  const oauthClientMetadataUrl = oauthOptions?.clientMetadataUrl?.trim() || void 0;
4412
4394
  const oauthScope = oauthOptions?.scope?.trim() || void 0;
@@ -4454,6 +4436,9 @@ function useMcp(options) {
4454
4436
  );
4455
4437
  const oauthClientConfig = derivedOAuthClientConfig;
4456
4438
  const autoProxyFallbackConfig = useMemo(() => {
4439
+ if (connectionMode === "direct" || connectionMode === "proxy") {
4440
+ return { enabled: false, proxyAddress: void 0 };
4441
+ }
4457
4442
  if (!autoProxyFallback) {
4458
4443
  return { enabled: false, proxyAddress: void 0 };
4459
4444
  }
@@ -4469,7 +4454,7 @@ function useMcp(options) {
4469
4454
  enabled: autoProxyFallback.enabled !== false && Boolean(proxyAddress),
4470
4455
  proxyAddress
4471
4456
  };
4472
- }, [autoProxyFallback, proxyConfig]);
4457
+ }, [autoProxyFallback, connectionMode, proxyConfig]);
4473
4458
  const autoReconnectConfig = useMemo(() => {
4474
4459
  if (autoReconnect === false) {
4475
4460
  return {
@@ -4505,20 +4490,33 @@ function useMcp(options) {
4505
4490
  const [effectiveProxyConfig, setEffectiveProxyConfig] = useState(void 0);
4506
4491
  useEffect(() => {
4507
4492
  setEffectiveProxyConfig(void 0);
4508
- }, [url, proxyConfig]);
4493
+ }, [
4494
+ url,
4495
+ requestedProxyAddress,
4496
+ connectionMode,
4497
+ autoProxyFallbackConfig.proxyAddress
4498
+ ]);
4509
4499
  const activeProxyConfig = useMemo(() => {
4510
- if (!effectiveProxyConfig?.proxyAddress) {
4511
- return proxyConfig;
4500
+ const hasCurrentAutoFallback = autoProxyFallbackConfig.enabled && effectiveProxyConfig?.proxyAddress === autoProxyFallbackConfig.proxyAddress;
4501
+ if (hasCurrentAutoFallback && effectiveProxyConfig) {
4502
+ const latestHeaders = proxyConfig?.headers ?? {};
4503
+ return {
4504
+ ...effectiveProxyConfig,
4505
+ headers: {
4506
+ ...latestHeaders,
4507
+ ...effectiveProxyConfig.headers ?? {}
4508
+ }
4509
+ };
4512
4510
  }
4513
- const latestHeaders = proxyConfig?.headers ?? {};
4514
- return {
4515
- ...effectiveProxyConfig,
4516
- headers: {
4517
- ...latestHeaders,
4518
- ...effectiveProxyConfig.headers ?? {}
4519
- }
4520
- };
4521
- }, [effectiveProxyConfig, proxyConfig]);
4511
+ const startsDirect = connectionMode === "auto" || connectionMode === "direct" || connectionMode === void 0 && autoProxyFallbackConfig.enabled;
4512
+ return startsDirect ? void 0 : proxyConfig;
4513
+ }, [
4514
+ effectiveProxyConfig,
4515
+ proxyConfig,
4516
+ connectionMode,
4517
+ autoProxyFallbackConfig.enabled,
4518
+ autoProxyFallbackConfig.proxyAddress
4519
+ ]);
4522
4520
  const gatewayUrl = activeProxyConfig?.proxyAddress;
4523
4521
  const proxyHeaders = activeProxyConfig?.headers ?? {};
4524
4522
  const effectiveOAuthUrl = useMemo(() => {
@@ -4850,8 +4848,9 @@ function useMcp(options) {
4850
4848
  clientInfo: mergedClientInfo,
4851
4849
  // Pass a fetch that scopes OAuth-proxy routing to this server's
4852
4850
  // transport/auth calls. getProxyFetch wraps `customFetch` (e.g. the
4853
- // OAuth retry fetch for scope step-up) when proxying, or returns it
4854
- // unchanged otherwise. Never mutates the global fetch.
4851
+ // OAuth retry fetch for scope step-up), bypasses the browser cache
4852
+ // for OAuth metadata, and optionally routes OAuth through the BFF.
4853
+ // It never mutates the global fetch.
4855
4854
  ...(() => {
4856
4855
  const scopedFetch = authProviderRef.current?.getProxyFetch?.(customFetch) ?? customFetch;
4857
4856
  return scopedFetch ? { fetch: scopedFetch } : {};
@@ -4989,9 +4988,22 @@ function useMcp(options) {
4989
4988
  });
4990
4989
  setTools(connection.tools || []);
4991
4990
  const [resourcesResult, promptsResult, templatesResult] = await Promise.all([
4992
- connection.listAllResources(),
4993
- connection.listPrompts(),
4994
- connection.supports("resources") ? connection.listResourceTemplates() : Promise.resolve({ resourceTemplates: [] })
4991
+ connection.listAllResources().catch((error2) => {
4992
+ addLog("warn", "Failed to load initial resources:", error2);
4993
+ return { resources: [] };
4994
+ }),
4995
+ connection.listPrompts().catch((error2) => {
4996
+ addLog("warn", "Failed to load initial prompts:", error2);
4997
+ return { prompts: [] };
4998
+ }),
4999
+ connection.supports("resources") ? connection.listResourceTemplates().catch((error2) => {
5000
+ addLog(
5001
+ "warn",
5002
+ "Failed to load initial resource templates:",
5003
+ error2
5004
+ );
5005
+ return { resourceTemplates: [] };
5006
+ }) : Promise.resolve({ resourceTemplates: [] })
4995
5007
  ]);
4996
5008
  if (!isMountedRef.current) {
4997
5009
  addLog(
@@ -5052,12 +5064,18 @@ function useMcp(options) {
5052
5064
  if (tokens?.access_token) {
5053
5065
  const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1e3 : void 0;
5054
5066
  let tokenEndpoint = null;
5067
+ let resource = null;
5055
5068
  let clientCreds = null;
5056
5069
  try {
5057
5070
  tokenEndpoint = await authProviderRef.current.getTokenEndpoint?.() ?? null;
5058
5071
  } catch {
5059
5072
  tokenEndpoint = null;
5060
5073
  }
5074
+ try {
5075
+ resource = await authProviderRef.current.getResource?.() ?? null;
5076
+ } catch {
5077
+ resource = null;
5078
+ }
5061
5079
  try {
5062
5080
  clientCreds = await authProviderRef.current.getClientCredentials?.() ?? null;
5063
5081
  } catch {
@@ -5074,6 +5092,7 @@ function useMcp(options) {
5074
5092
  refresh_token: tokens.refresh_token,
5075
5093
  scope: tokens.scope,
5076
5094
  ...tokenEndpoint ? { token_endpoint: tokenEndpoint } : {},
5095
+ ...resource ? { resource } : {},
5077
5096
  ...clientCreds?.client_id ? { client_id: clientCreds.client_id } : {},
5078
5097
  ...clientCreds?.client_secret ? { client_secret: clientCreds.client_secret } : {}
5079
5098
  });
@@ -5318,19 +5337,20 @@ function useMcp(options) {
5318
5337
  addLog("info", "Triggering fresh OAuth authorization...");
5319
5338
  const parsedUrl = new URL(url);
5320
5339
  const baseUrl = parsedUrl.origin + parsedUrl.pathname.replace(/\/+$/, "");
5321
- try {
5322
- await auth2(freshAuthProvider, {
5323
- serverUrl: baseUrl,
5324
- fetchFn: freshAuthProvider.getProxyFetch?.()
5325
- });
5340
+ const authResult = await auth2(freshAuthProvider, {
5341
+ serverUrl: baseUrl,
5342
+ fetchFn: freshAuthProvider.getProxyFetch?.()
5343
+ });
5344
+ if (authResult === "AUTHORIZED") {
5326
5345
  addLog("info", "OAuth flow completed (tokens obtained)");
5327
- } catch (err) {
5328
- addLog(
5329
- "info",
5330
- "OAuth flow initiated (popup/redirect):",
5331
- err instanceof Error ? err.message : "Redirecting..."
5332
- );
5346
+ connectingRef.current = false;
5347
+ connectRef.current?.();
5348
+ return;
5333
5349
  }
5350
+ if (authResult !== "REDIRECT") {
5351
+ throw new Error(`Unexpected OAuth auth() result: ${authResult}`);
5352
+ }
5353
+ addLog("info", "OAuth authorization redirect initiated");
5334
5354
  const newAuthUrl = freshAuthProvider.getLastAttemptedAuthUrl?.();
5335
5355
  if (newAuthUrl) {
5336
5356
  setAuthUrl(newAuthUrl);
@@ -5396,11 +5416,8 @@ function useMcp(options) {
5396
5416
  }
5397
5417
  } catch (authError) {
5398
5418
  if (!isMountedRef.current) return;
5399
- setState("pending_auth");
5400
- addLog(
5401
- "error",
5402
- `Manual authentication failed: ${authError instanceof Error ? authError.message : String(authError)}`
5403
- );
5419
+ const error2 = authError instanceof Error ? authError : new Error(String(authError));
5420
+ failConnection(`Manual authentication failed: ${error2.message}`, error2);
5404
5421
  }
5405
5422
  } else if (currentState === "authenticating") {
5406
5423
  addLog(
@@ -5608,6 +5625,7 @@ function useMcp(options) {
5608
5625
  // Triggers reconnection when proxy fallback changes OAuth URL
5609
5626
  proxyConfig,
5610
5627
  // Triggers reconnection when proxy config (including headers) changes
5628
+ autoProxyFallbackConfig.proxyAddress,
5611
5629
  providedAuthProvider
5612
5630
  ]);
5613
5631
  const retryRef = useRef(retry);
@@ -6881,6 +6899,7 @@ import React2, {
6881
6899
  memo,
6882
6900
  useCallback as useCallback6,
6883
6901
  useEffect as useEffect5,
6902
+ useMemo as useMemo3,
6884
6903
  useRef as useRef4,
6885
6904
  useState as useState4
6886
6905
  } from "react";
@@ -6903,6 +6922,53 @@ function parseCustomProps(customProps) {
6903
6922
  return parsed;
6904
6923
  }
6905
6924
 
6925
+ // src/react/view/inject-openai-file-apis.ts
6926
+ var OPENAI_FILE_APIS_SCRIPT = `<script>
6927
+ (function () {
6928
+ var files = new Map();
6929
+ window.openai = window.openai || {};
6930
+ window.openai.uploadFile = async function (file) {
6931
+ var fileId = crypto.randomUUID();
6932
+ files.set(fileId, file);
6933
+ return { fileId: fileId };
6934
+ };
6935
+ window.openai.getFileDownloadUrl = async function (ref) {
6936
+ var file = files.get(ref.fileId);
6937
+ if (!file) {
6938
+ throw new Error("File not found: " + ref.fileId);
6939
+ }
6940
+ return { downloadUrl: URL.createObjectURL(file) };
6941
+ };
6942
+ })();
6943
+ </script>`;
6944
+ function injectOpenAiFileApis(html) {
6945
+ if (html.includes("<head>")) {
6946
+ return html.replace("<head>", "<head>" + OPENAI_FILE_APIS_SCRIPT);
6947
+ }
6948
+ if (html.includes("<HEAD>")) {
6949
+ return html.replace("<HEAD>", "<HEAD>" + OPENAI_FILE_APIS_SCRIPT);
6950
+ }
6951
+ if (html.includes("<html>")) {
6952
+ return html.replace(
6953
+ "<html>",
6954
+ "<html><head>" + OPENAI_FILE_APIS_SCRIPT + "</head>"
6955
+ );
6956
+ }
6957
+ if (html.includes("<HTML>")) {
6958
+ return html.replace(
6959
+ "<HTML>",
6960
+ "<HTML><head>" + OPENAI_FILE_APIS_SCRIPT + "</head>"
6961
+ );
6962
+ }
6963
+ if (html.includes("<!DOCTYPE") || html.includes("<!doctype")) {
6964
+ return html.replace(
6965
+ /(<!DOCTYPE[^>]*>|<!doctype[^>]*>)/i,
6966
+ "$1<head>" + OPENAI_FILE_APIS_SCRIPT + "</head>"
6967
+ );
6968
+ }
6969
+ return OPENAI_FILE_APIS_SCRIPT + html;
6970
+ }
6971
+
6906
6972
  // src/react/view/resolve-view-resource.ts
6907
6973
  function resolveViewResource(options) {
6908
6974
  const { resourceResult, listingResource, cspMode, resourceUri } = options;
@@ -7252,9 +7318,9 @@ function buildSandboxProxyBlobHtml(search) {
7252
7318
  // src/react/view/use-display-mode.ts
7253
7319
  import { useCallback as useCallback5, useEffect as useEffect4 } from "react";
7254
7320
  var SHELL_BASE = "w-full h-full min-h-0 bg-background flex flex-col [&:fullscreen]:h-full [&:fullscreen]:w-full [&:fullscreen]:bg-background";
7255
- var WIDGET_FULLSCREEN_OVERLAY_CLASSES = `fixed inset-0 z-[100] ${SHELL_BASE}`;
7321
+ var WIDGET_FULLSCREEN_OVERLAY_CLASSES = `fixed inset-0 z-[200] ${SHELL_BASE}`;
7256
7322
  var WIDGET_PIP_SHELL_CLASSES = [
7257
- "fixed top-4 left-1/2 -translate-x-1/2 z-[100]",
7323
+ "fixed top-4 left-1/2 -translate-x-1/2 z-[200]",
7258
7324
  "rounded-3xl w-full min-w-[300px] h-[400px]",
7259
7325
  "shadow-2xl border overflow-hidden",
7260
7326
  "bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80",
@@ -7312,7 +7378,8 @@ function useViewDisplayModeControls({
7312
7378
  }
7313
7379
  var VIEW_DIMENSIONS = {
7314
7380
  PIP_MAX_WIDTH: 700,
7315
- DEFAULT_HEIGHT: 400
7381
+ DEFAULT_HEIGHT: 400,
7382
+ FULLSCREEN_HEADER_HEIGHT: 50
7316
7383
  };
7317
7384
 
7318
7385
  // src/react/view/view-detection.ts
@@ -7339,8 +7406,28 @@ var DEFAULT_HOST_CAPABILITIES = {
7339
7406
  serverTools: {},
7340
7407
  serverResources: {},
7341
7408
  logging: {},
7342
- updateModelContext: { text: {} }
7409
+ updateModelContext: { text: {} },
7410
+ // ponytail: always advertised; bridge.onmessage no-ops when onMessage unset
7411
+ message: { text: {} }
7343
7412
  };
7413
+ function CloseIcon() {
7414
+ return /* @__PURE__ */ React2.createElement(
7415
+ "svg",
7416
+ {
7417
+ width: "14",
7418
+ height: "14",
7419
+ viewBox: "0 0 24 24",
7420
+ fill: "none",
7421
+ stroke: "currentColor",
7422
+ strokeWidth: "2",
7423
+ strokeLinecap: "round",
7424
+ strokeLinejoin: "round",
7425
+ "aria-hidden": true
7426
+ },
7427
+ /* @__PURE__ */ React2.createElement("path", { d: "M18 6 6 18" }),
7428
+ /* @__PURE__ */ React2.createElement("path", { d: "m6 6 12 12" })
7429
+ );
7430
+ }
7344
7431
  function waitForSandboxProxyReady(iframe) {
7345
7432
  return new Promise((resolve) => {
7346
7433
  const listener = (event) => {
@@ -7401,6 +7488,10 @@ function ViewRendererBase({
7401
7488
  onResourceResolved,
7402
7489
  wrapTransport,
7403
7490
  toolCallTimeout = DEFAULT_TOOL_CALL_TIMEOUT,
7491
+ mockOpenAiFileApis = false,
7492
+ onInlineHeightChange,
7493
+ fullscreenHeader,
7494
+ renderFullscreenClose,
7404
7495
  className,
7405
7496
  testId = "mcp-app-frame",
7406
7497
  invoking,
@@ -7409,6 +7500,9 @@ function ViewRendererBase({
7409
7500
  const iframeRef = useRef4(null);
7410
7501
  const bridgeRef = useRef4(null);
7411
7502
  const containerRef = useRef4(null);
7503
+ const pendingBlobRevocationsRef = useRef4(
7504
+ /* @__PURE__ */ new Map()
7505
+ );
7412
7506
  const connectionRef = useRef4(
7413
7507
  source.kind === "live" ? source.connection : null
7414
7508
  );
@@ -7422,12 +7516,19 @@ function ViewRendererBase({
7422
7516
  );
7423
7517
  const [internalDisplayMode, setInternalDisplayMode] = useState4("inline");
7424
7518
  const displayMode = displayModeProp ?? internalDisplayMode;
7425
- const hostContextRef = useRef4(hostContext);
7426
- hostContextRef.current = hostContext;
7519
+ const effectiveHostContext = useMemo3(() => {
7520
+ if (!hostContext) return hostContext;
7521
+ if (hostContext.displayMode === displayMode) return hostContext;
7522
+ return { ...hostContext, displayMode };
7523
+ }, [hostContext, displayMode]);
7524
+ const hostContextRef = useRef4(effectiveHostContext);
7525
+ hostContextRef.current = effectiveHostContext;
7427
7526
  const onMessageRef = useRef4(onMessage);
7428
7527
  onMessageRef.current = onMessage;
7429
7528
  const toolInputRef = useRef4(toolInput);
7430
7529
  toolInputRef.current = toolInput;
7530
+ const partialToolInputRef = useRef4(partialToolInput);
7531
+ partialToolInputRef.current = partialToolInput;
7431
7532
  const toolOutputRef = useRef4(toolOutput);
7432
7533
  toolOutputRef.current = toolOutput;
7433
7534
  const customPropsRef = useRef4(customProps);
@@ -7446,10 +7547,14 @@ function ViewRendererBase({
7446
7547
  onReadyRef.current = onReady;
7447
7548
  const onLifecycleChangeRef = useRef4(onLifecycleChange);
7448
7549
  onLifecycleChangeRef.current = onLifecycleChange;
7550
+ const onInlineHeightChangeRef = useRef4(onInlineHeightChange);
7551
+ onInlineHeightChangeRef.current = onInlineHeightChange;
7449
7552
  const sandboxUrlRef = useRef4(sandboxUrl);
7450
7553
  sandboxUrlRef.current = sandboxUrl;
7451
7554
  const cspModeRef = useRef4(cspMode);
7452
7555
  cspModeRef.current = cspMode;
7556
+ const mockOpenAiFileApisRef = useRef4(mockOpenAiFileApis);
7557
+ mockOpenAiFileApisRef.current = mockOpenAiFileApis;
7453
7558
  const resolveSandboxUrl = useCallback6((next) => {
7454
7559
  const custom = sandboxUrlRef.current;
7455
7560
  if (custom) {
@@ -7553,10 +7658,18 @@ function ViewRendererBase({
7553
7658
  }, [source.kind, liveResourceUri, preloadedHtml, cspMode, resolveSandboxUrl]);
7554
7659
  useEffect5(() => {
7555
7660
  const url = activeSandboxUrl;
7661
+ if (!url || url.protocol !== "blob:") return;
7662
+ const pending = pendingBlobRevocationsRef.current.get(url.href);
7663
+ if (pending) {
7664
+ clearTimeout(pending);
7665
+ pendingBlobRevocationsRef.current.delete(url.href);
7666
+ }
7556
7667
  return () => {
7557
- if (url?.protocol === "blob:") {
7668
+ const timer = setTimeout(() => {
7558
7669
  URL.revokeObjectURL(url.href);
7559
- }
7670
+ pendingBlobRevocationsRef.current.delete(url.href);
7671
+ }, 1e3);
7672
+ pendingBlobRevocationsRef.current.set(url.href, timer);
7560
7673
  };
7561
7674
  }, [activeSandboxUrl]);
7562
7675
  const isBlobSandbox = activeSandboxUrl?.protocol === "blob:";
@@ -7617,7 +7730,14 @@ function ViewRendererBase({
7617
7730
  iframe.setAttribute("allow", allowAttribute);
7618
7731
  }
7619
7732
  const readyPromise = waitForSandboxProxyReady(iframe);
7620
- iframe.src = activeSandboxUrl.href;
7733
+ if (activeSandboxUrl.protocol === "blob:") {
7734
+ const response = await fetch(activeSandboxUrl.href);
7735
+ const sandboxHtml = await response.text();
7736
+ if (disposed) return;
7737
+ iframe.srcdoc = sandboxHtml;
7738
+ } else {
7739
+ iframe.src = activeSandboxUrl.href;
7740
+ }
7621
7741
  await readyPromise;
7622
7742
  if (disposed) return;
7623
7743
  const capabilities = {
@@ -7703,7 +7823,10 @@ function ViewRendererBase({
7703
7823
  height
7704
7824
  }) => {
7705
7825
  if (displayModeRef.current !== "inline") return;
7706
- if (height !== void 0) setInlineHeight(height);
7826
+ if (height !== void 0) {
7827
+ setInlineHeight(height);
7828
+ onInlineHeightChangeRef.current?.(height);
7829
+ }
7707
7830
  };
7708
7831
  const initPromise = hookInitialized(bridge);
7709
7832
  let transport = new PostMessageTransport(
@@ -7716,7 +7839,7 @@ function ViewRendererBase({
7716
7839
  await bridge.connect(transport);
7717
7840
  if (disposed) return;
7718
7841
  await bridge.sendSandboxResourceReady({
7719
- html: resolved.html,
7842
+ html: mockOpenAiFileApisRef.current ? injectOpenAiFileApis(resolved.html) : resolved.html,
7720
7843
  csp: resolved.csp,
7721
7844
  permissions: resolved.permissions
7722
7845
  });
@@ -7725,11 +7848,18 @@ function ViewRendererBase({
7725
7848
  bridgeRef.current = bridge;
7726
7849
  setInitCount((c) => c + 1);
7727
7850
  onLifecycleChangeRef.current?.({ status: "initialized" });
7728
- const mergedArgs = {
7729
- ...toolInputRef.current,
7730
- ...parseCustomProps(customPropsRef.current)
7731
- };
7732
- bridge.sendToolInput({ arguments: mergedArgs });
7851
+ const currentPartialToolInput = partialToolInputRef.current;
7852
+ if (currentPartialToolInput) {
7853
+ bridge.sendToolInputPartial({
7854
+ arguments: currentPartialToolInput
7855
+ });
7856
+ } else {
7857
+ const mergedArgs = {
7858
+ ...toolInputRef.current,
7859
+ ...parseCustomProps(customPropsRef.current)
7860
+ };
7861
+ bridge.sendToolInput({ arguments: mergedArgs });
7862
+ }
7733
7863
  const toolResultPayload = buildToolResultPayload(
7734
7864
  toolOutputRef.current,
7735
7865
  customPropsRef.current
@@ -7778,13 +7908,14 @@ function ViewRendererBase({
7778
7908
  cspMode,
7779
7909
  viewId,
7780
7910
  wrapTransport,
7781
- toolCallTimeout
7911
+ toolCallTimeout,
7912
+ mockOpenAiFileApis
7782
7913
  ]);
7783
7914
  useEffect5(() => {
7784
7915
  const bridge = bridgeRef.current;
7785
- if (!bridge || initCount === 0 || !hostContext) return;
7786
- bridge.setHostContext(hostContext);
7787
- }, [hostContext, initCount]);
7916
+ if (!bridge || initCount === 0 || !effectiveHostContext) return;
7917
+ bridge.setHostContext(effectiveHostContext);
7918
+ }, [effectiveHostContext, initCount]);
7788
7919
  useEffect5(() => {
7789
7920
  const bridge = bridgeRef.current;
7790
7921
  if (!bridge || initCount === 0 || !partialToolInput) return;
@@ -7792,13 +7923,13 @@ function ViewRendererBase({
7792
7923
  }, [initCount, partialToolInput]);
7793
7924
  useEffect5(() => {
7794
7925
  const bridge = bridgeRef.current;
7795
- if (!bridge || initCount === 0) return;
7926
+ if (!bridge || initCount === 0 || partialToolInput) return;
7796
7927
  const mergedArgs = {
7797
7928
  ...toolInput,
7798
7929
  ...parseCustomProps(customProps)
7799
7930
  };
7800
7931
  bridge.sendToolInput({ arguments: mergedArgs });
7801
- }, [initCount, toolInput, customProps]);
7932
+ }, [initCount, toolInput, partialToolInput, customProps]);
7802
7933
  useEffect5(() => {
7803
7934
  const bridge = bridgeRef.current;
7804
7935
  if (!bridge || initCount === 0) return;
@@ -7822,6 +7953,7 @@ function ViewRendererBase({
7822
7953
  const timer = setTimeout(() => setShowSpinner(false), 300);
7823
7954
  return () => clearTimeout(timer);
7824
7955
  }, [initCount, showSpinner]);
7956
+ const showHostBorder = resolved !== null && resolved.prefersBorder && displayMode !== "fullscreen";
7825
7957
  if (loadError) {
7826
7958
  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)));
7827
7959
  }
@@ -7839,29 +7971,69 @@ function ViewRendererBase({
7839
7971
  "div",
7840
7972
  {
7841
7973
  ref: containerRef,
7842
- className: containerClassName,
7974
+ className: isFullscreen ? `${containerClassName} flex flex-col` : containerClassName,
7843
7975
  style: isPip ? {
7844
7976
  height: VIEW_DIMENSIONS.DEFAULT_HEIGHT,
7845
7977
  maxWidth: VIEW_DIMENSIONS.PIP_MAX_WIDTH,
7846
7978
  zIndex: 100
7847
7979
  } : isFullscreen ? { zIndex: 100 } : void 0
7848
7980
  },
7849
- (isFullscreen || isPip) && /* @__PURE__ */ React2.createElement(
7981
+ isFullscreen && // ponytail: inspector Tailwind may not emit client arbitrary classes
7982
+ // (h-[50px], grid-cols-[auto_1fr_auto]) — use inline layout instead.
7983
+ /* @__PURE__ */ React2.createElement(
7984
+ "header",
7985
+ {
7986
+ className: "grid shrink-0 items-center border-b border-zinc-200 bg-background px-3 dark:border-zinc-700",
7987
+ style: {
7988
+ height: VIEW_DIMENSIONS.FULLSCREEN_HEADER_HEIGHT,
7989
+ gridTemplateColumns: "auto 1fr auto"
7990
+ }
7991
+ },
7992
+ renderFullscreenClose ? renderFullscreenClose({
7993
+ onClick: () => void handleDisplayModeChange("inline"),
7994
+ "data-testid": "debugger-exit-fullscreen-button",
7995
+ "aria-label": "Exit fullscreen"
7996
+ }) : /* @__PURE__ */ React2.createElement(
7997
+ "button",
7998
+ {
7999
+ type: "button",
8000
+ "data-testid": "debugger-exit-fullscreen-button",
8001
+ "aria-label": "Exit fullscreen",
8002
+ 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",
8003
+ onClick: () => void handleDisplayModeChange("inline")
8004
+ },
8005
+ /* @__PURE__ */ React2.createElement(CloseIcon, null)
8006
+ ),
8007
+ /* @__PURE__ */ React2.createElement("div", { className: "flex min-w-0 items-center justify-center gap-2 px-2" }, fullscreenHeader?.iconUrl ? /* @__PURE__ */ React2.createElement(
8008
+ "img",
8009
+ {
8010
+ src: fullscreenHeader.iconUrl,
8011
+ alt: "",
8012
+ className: "size-6 shrink-0 rounded-md object-contain"
8013
+ }
8014
+ ) : null, /* @__PURE__ */ React2.createElement("span", { className: "truncate text-sm font-medium text-foreground" }, fullscreenHeader?.title ?? toolName)),
8015
+ /* @__PURE__ */ React2.createElement("div", { className: "size-8 shrink-0", "aria-hidden": true })
8016
+ ),
8017
+ isPip && (renderFullscreenClose ? /* @__PURE__ */ React2.createElement("div", { className: "absolute right-3 top-3", style: { zIndex: 110 } }, renderFullscreenClose({
8018
+ onClick: () => void handleDisplayModeChange("inline"),
8019
+ "data-testid": "debugger-exit-pip-button",
8020
+ "aria-label": "Exit picture-in-picture"
8021
+ })) : /* @__PURE__ */ React2.createElement(
7850
8022
  "button",
7851
8023
  {
7852
8024
  type: "button",
7853
- "data-testid": isFullscreen ? "debugger-exit-fullscreen-button" : "debugger-exit-pip-button",
7854
- "aria-label": isFullscreen ? "Exit fullscreen" : "Exit picture-in-picture",
7855
- 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",
8025
+ "data-testid": "debugger-exit-pip-button",
8026
+ "aria-label": "Exit picture-in-picture",
8027
+ 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",
7856
8028
  style: { zIndex: 110 },
7857
8029
  onClick: () => void handleDisplayModeChange("inline")
7858
8030
  },
7859
- "\xD7"
7860
- ),
8031
+ /* @__PURE__ */ React2.createElement(CloseIcon, null)
8032
+ )),
7861
8033
  /* @__PURE__ */ React2.createElement(
7862
8034
  "div",
7863
8035
  {
7864
- 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"
8036
+ 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"
7865
8037
  },
7866
8038
  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")),
7867
8039
  !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),
@@ -7878,10 +8050,7 @@ function ViewRendererBase({
7878
8050
  {
7879
8051
  ref: iframeRef,
7880
8052
  title: `MCP App: ${toolName}`,
7881
- className: "w-full h-full border-0 bg-transparent",
7882
- style: {
7883
- border: resolved.prefersBorder && displayMode !== "fullscreen" ? void 0 : "none"
7884
- }
8053
+ className: showHostBorder ? "w-full h-full bg-transparent border border-border rounded-xl" : "w-full h-full bg-transparent border-0"
7885
8054
  }
7886
8055
  )
7887
8056
  )
@@ -7902,6 +8071,10 @@ function viewRendererAreEqual(prev, next) {
7902
8071
  if (prev.hostContext !== next.hostContext) return false;
7903
8072
  if (prev.hostCapabilities !== next.hostCapabilities) return false;
7904
8073
  if (prev.cspMode !== next.cspMode) return false;
8074
+ if (prev.mockOpenAiFileApis !== next.mockOpenAiFileApis) return false;
8075
+ if (prev.onInlineHeightChange !== next.onInlineHeightChange) return false;
8076
+ if (prev.fullscreenHeader !== next.fullscreenHeader) return false;
8077
+ if (prev.renderFullscreenClose !== next.renderFullscreenClose) return false;
7905
8078
  if (prev.className !== next.className) return false;
7906
8079
  if (prev.onReady !== next.onReady) return false;
7907
8080
  if (prev.onLifecycleChange !== next.onLifecycleChange) return false;