@mcp-use/client 2.0.0-beta.12 → 2.0.0-beta.13

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.
@@ -251,6 +251,67 @@ var init_rpc_logger = __esm({
251
251
  }
252
252
  });
253
253
 
254
+ // src/react/types.ts
255
+ var PERSISTED_SERVER_CONFIG_KEYS = [
256
+ "url",
257
+ "displayName",
258
+ "enabled",
259
+ "oauthProxyUrl",
260
+ "connectionMode",
261
+ "autoProxyFallback",
262
+ "callbackUrl",
263
+ "storageKeyPrefix",
264
+ "logLevel",
265
+ "autoRetry",
266
+ "autoReconnect",
267
+ "reconnectionOptions",
268
+ "popupFeatures",
269
+ "preventAutoAuth",
270
+ "useRedirectFlow",
271
+ "protocolNegotiation",
272
+ "timeout",
273
+ "clientInfo"
274
+ ];
275
+ function pickPersistedServerConfig(source) {
276
+ const out = {};
277
+ for (const key of PERSISTED_SERVER_CONFIG_KEYS) {
278
+ const value = source[key];
279
+ if (value !== void 0) {
280
+ out[key] = value;
281
+ }
282
+ }
283
+ if (source.proxyConfig?.proxyAddress !== void 0) {
284
+ out.proxyConfig = { proxyAddress: source.proxyConfig.proxyAddress };
285
+ }
286
+ if (source.oauth) {
287
+ const oauth = {};
288
+ if (source.oauth.clientId !== void 0) {
289
+ oauth.clientId = source.oauth.clientId;
290
+ }
291
+ if (source.oauth.clientMetadataUrl !== void 0) {
292
+ oauth.clientMetadataUrl = source.oauth.clientMetadataUrl;
293
+ }
294
+ if (source.oauth.scope !== void 0) {
295
+ oauth.scope = source.oauth.scope;
296
+ }
297
+ if (Object.keys(oauth).length > 0) {
298
+ out.oauth = oauth;
299
+ }
300
+ }
301
+ return out;
302
+ }
303
+ function pickLiveServerConfig(source) {
304
+ return {
305
+ ...pickPersistedServerConfig(source),
306
+ ...source.headers !== void 0 ? { headers: source.headers } : {},
307
+ ...source.proxyConfig !== void 0 ? { proxyConfig: source.proxyConfig } : {},
308
+ ...source.clientOptions !== void 0 ? { clientOptions: source.clientOptions } : {}
309
+ };
310
+ }
311
+ function toPersistedServerConfig(config) {
312
+ return pickPersistedServerConfig(config);
313
+ }
314
+
254
315
  // src/react/useMcp.ts
255
316
  import { auth as auth2 } from "@modelcontextprotocol/client";
256
317
 
@@ -1473,7 +1534,7 @@ var HttpConnector = class extends BaseConnector {
1473
1534
  };
1474
1535
 
1475
1536
  // src/utils/version.ts
1476
- var VERSION = "2.0.0-beta.11";
1537
+ var VERSION = "2.0.0-beta.13";
1477
1538
  function getPackageVersion() {
1478
1539
  return VERSION;
1479
1540
  }
@@ -1544,6 +1605,186 @@ function resolveClientOptions(clientOptions) {
1544
1605
  };
1545
1606
  }
1546
1607
 
1608
+ // src/auth/storage.ts
1609
+ var AUTH_CRYPTO_DATABASE = "mcp-use-oauth-crypto";
1610
+ var AUTH_CRYPTO_STORE = "keys";
1611
+ var AUTH_CRYPTO_KEY = "aes-gcm-v1";
1612
+ var textEncoder = new TextEncoder();
1613
+ var textDecoder = new TextDecoder();
1614
+ var LocalStorageKVStore = class {
1615
+ fallback = /* @__PURE__ */ new Map();
1616
+ keyPromise;
1617
+ durable = true;
1618
+ async get(key) {
1619
+ if (!this.durable) return this.fallback.get(key) ?? null;
1620
+ let stored;
1621
+ try {
1622
+ stored = localStorage.getItem(key);
1623
+ } catch {
1624
+ this.durable = false;
1625
+ return this.fallback.get(key) ?? null;
1626
+ }
1627
+ if (stored === null) return null;
1628
+ const envelope = parseEncryptedEnvelope(stored);
1629
+ if (!envelope) {
1630
+ await this.set(key, stored);
1631
+ return stored;
1632
+ }
1633
+ try {
1634
+ const cryptoKey = await this.getCryptoKey();
1635
+ const plaintext = await globalThis.crypto.subtle.decrypt(
1636
+ {
1637
+ name: "AES-GCM",
1638
+ iv: decodeBase64(envelope.iv),
1639
+ additionalData: textEncoder.encode(key)
1640
+ },
1641
+ cryptoKey,
1642
+ decodeBase64(envelope.ciphertext)
1643
+ );
1644
+ return textDecoder.decode(plaintext);
1645
+ } catch {
1646
+ await this.remove(key);
1647
+ return null;
1648
+ }
1649
+ }
1650
+ async set(key, value) {
1651
+ if (!this.durable) {
1652
+ this.fallback.set(key, value);
1653
+ return;
1654
+ }
1655
+ try {
1656
+ const cryptoKey = await this.getCryptoKey();
1657
+ const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));
1658
+ const ciphertext = await globalThis.crypto.subtle.encrypt(
1659
+ {
1660
+ name: "AES-GCM",
1661
+ iv,
1662
+ additionalData: textEncoder.encode(key)
1663
+ },
1664
+ cryptoKey,
1665
+ textEncoder.encode(value)
1666
+ );
1667
+ const envelope = {
1668
+ v: 1,
1669
+ alg: "A256GCM",
1670
+ iv: encodeBase64(iv),
1671
+ ciphertext: encodeBase64(new Uint8Array(ciphertext))
1672
+ };
1673
+ localStorage.setItem(key, JSON.stringify(envelope));
1674
+ this.fallback.delete(key);
1675
+ } catch {
1676
+ this.durable = false;
1677
+ try {
1678
+ localStorage.removeItem(key);
1679
+ } catch {
1680
+ }
1681
+ this.fallback.set(key, value);
1682
+ }
1683
+ }
1684
+ remove(key) {
1685
+ this.fallback.delete(key);
1686
+ try {
1687
+ localStorage.removeItem(key);
1688
+ } catch {
1689
+ this.durable = false;
1690
+ }
1691
+ }
1692
+ keys() {
1693
+ const out = new Set(this.fallback.keys());
1694
+ if (this.durable) {
1695
+ try {
1696
+ for (let i = 0; i < localStorage.length; i++) {
1697
+ const key = localStorage.key(i);
1698
+ if (key) out.add(key);
1699
+ }
1700
+ } catch {
1701
+ this.durable = false;
1702
+ }
1703
+ }
1704
+ return [...out];
1705
+ }
1706
+ getCryptoKey() {
1707
+ this.keyPromise ??= getOrCreateCryptoKey();
1708
+ return this.keyPromise;
1709
+ }
1710
+ };
1711
+ function parseEncryptedEnvelope(value) {
1712
+ try {
1713
+ const parsed = JSON.parse(value);
1714
+ if (!parsed || typeof parsed !== "object" || !("v" in parsed) || parsed.v !== 1 || !("alg" in parsed) || parsed.alg !== "A256GCM" || !("iv" in parsed) || typeof parsed.iv !== "string" || !("ciphertext" in parsed) || typeof parsed.ciphertext !== "string") {
1715
+ return void 0;
1716
+ }
1717
+ return parsed;
1718
+ } catch {
1719
+ return void 0;
1720
+ }
1721
+ }
1722
+ async function getOrCreateCryptoKey() {
1723
+ if (!globalThis.crypto?.subtle || typeof indexedDB === "undefined") {
1724
+ throw new Error("Durable browser cryptography is unavailable");
1725
+ }
1726
+ const candidate = await globalThis.crypto.subtle.generateKey(
1727
+ { name: "AES-GCM", length: 256 },
1728
+ false,
1729
+ ["encrypt", "decrypt"]
1730
+ );
1731
+ const database = await openCryptoDatabase();
1732
+ try {
1733
+ return await new Promise((resolve, reject) => {
1734
+ const transaction = database.transaction(AUTH_CRYPTO_STORE, "readwrite");
1735
+ const store = transaction.objectStore(AUTH_CRYPTO_STORE);
1736
+ const request = store.get(AUTH_CRYPTO_KEY);
1737
+ let selected;
1738
+ request.onsuccess = () => {
1739
+ selected = request.result;
1740
+ if (!selected) {
1741
+ selected = candidate;
1742
+ store.put(candidate, AUTH_CRYPTO_KEY);
1743
+ }
1744
+ };
1745
+ request.onerror = () => reject(request.error);
1746
+ transaction.oncomplete = () => {
1747
+ if (selected) resolve(selected);
1748
+ else reject(new Error("OAuth encryption key was not initialized"));
1749
+ };
1750
+ transaction.onerror = () => reject(transaction.error);
1751
+ transaction.onabort = () => reject(transaction.error);
1752
+ });
1753
+ } finally {
1754
+ database.close();
1755
+ }
1756
+ }
1757
+ function openCryptoDatabase() {
1758
+ return new Promise((resolve, reject) => {
1759
+ const request = indexedDB.open(AUTH_CRYPTO_DATABASE, 1);
1760
+ request.onupgradeneeded = () => {
1761
+ const database = request.result;
1762
+ if (!database.objectStoreNames.contains(AUTH_CRYPTO_STORE)) {
1763
+ database.createObjectStore(AUTH_CRYPTO_STORE);
1764
+ }
1765
+ };
1766
+ request.onsuccess = () => resolve(request.result);
1767
+ request.onerror = () => reject(request.error);
1768
+ request.onblocked = () => reject(new Error("OAuth encryption database is blocked"));
1769
+ });
1770
+ }
1771
+ function encodeBase64(bytes) {
1772
+ let binary = "";
1773
+ for (const byte of bytes) binary += String.fromCharCode(byte);
1774
+ return btoa(binary);
1775
+ }
1776
+ function decodeBase64(value) {
1777
+ const binary = atob(value);
1778
+ const bytes = new Uint8Array(binary.length);
1779
+ for (let index = 0; index < binary.length; index++) {
1780
+ bytes[index] = binary.charCodeAt(index);
1781
+ }
1782
+ return bytes;
1783
+ }
1784
+
1785
+ // src/auth/session-store.ts
1786
+ import { validateClientMetadataUrl } from "@modelcontextprotocol/client";
1787
+
1547
1788
  // src/auth/url.ts
1548
1789
  function sanitizeUrl(raw) {
1549
1790
  const abort = () => {
@@ -1568,29 +1809,7 @@ function sanitizeParam([k, v]) {
1568
1809
  return `${encodeURIComponent(k)}${v.length > 0 ? `=${encodeURIComponent(v)}` : ""}`;
1569
1810
  }
1570
1811
 
1571
- // src/auth/storage.ts
1572
- var LocalStorageKVStore = class {
1573
- get(key) {
1574
- return localStorage.getItem(key);
1575
- }
1576
- set(key, value) {
1577
- localStorage.setItem(key, value);
1578
- }
1579
- remove(key) {
1580
- localStorage.removeItem(key);
1581
- }
1582
- keys() {
1583
- const out = [];
1584
- for (let i = 0; i < localStorage.length; i++) {
1585
- const k = localStorage.key(i);
1586
- if (k) out.push(k);
1587
- }
1588
- return out;
1589
- }
1590
- };
1591
-
1592
1812
  // src/auth/session-store.ts
1593
- import { validateClientMetadataUrl } from "@modelcontextprotocol/client";
1594
1813
  var OAuthSessionStore = class _OAuthSessionStore {
1595
1814
  serverUrl;
1596
1815
  storageKeyPrefix;
@@ -1902,16 +2121,25 @@ async function serializeBody(body) {
1902
2121
  if (body instanceof Blob) return await body.text();
1903
2122
  return body;
1904
2123
  }
2124
+ function trimTrailingSlashes(value) {
2125
+ let end = value.length;
2126
+ while (end > 0 && value.charCodeAt(end - 1) === 47) {
2127
+ end--;
2128
+ }
2129
+ return value.slice(0, end);
2130
+ }
1905
2131
  var BrowserOAuthClientProvider = class {
1906
2132
  serverUrl;
1907
2133
  staticClientInfo;
1908
2134
  session;
2135
+ storage;
1909
2136
  // Browser-only state
1910
2137
  preventAutoAuth;
1911
2138
  useRedirectFlow;
1912
2139
  oauthProxyUrl;
1913
2140
  connectionUrl;
1914
2141
  proxyOAuthRequests;
2142
+ lastAttemptedAuthUrl = null;
1915
2143
  onPopupWindow;
1916
2144
  constructor(serverUrl, options = {}) {
1917
2145
  if (options.staticClientInfo?.client_secret) {
@@ -1920,10 +2148,11 @@ var BrowserOAuthClientProvider = class {
1920
2148
  );
1921
2149
  }
1922
2150
  this.serverUrl = serverUrl;
2151
+ this.storage = new LocalStorageKVStore();
1923
2152
  this.session = new OAuthSessionStore(
1924
2153
  serverUrl,
1925
2154
  { ...options, allowClientSecret: false },
1926
- new LocalStorageKVStore()
2155
+ this.storage
1927
2156
  );
1928
2157
  this.preventAutoAuth = options.preventAutoAuth;
1929
2158
  this.useRedirectFlow = options.useRedirectFlow;
@@ -1988,8 +2217,8 @@ var BrowserOAuthClientProvider = class {
1988
2217
  const [doc, ...suffixParts] = rest.split("/");
1989
2218
  if (!doc) return url;
1990
2219
  const suffix = suffixParts.length ? `/${suffixParts.join("/")}` : "";
1991
- const connectionPath = connection.pathname.replace(/\/+$/, "");
1992
- const targetPath = target.pathname.replace(/\/+$/, "");
2220
+ const connectionPath = trimTrailingSlashes(connection.pathname);
2221
+ const targetPath = trimTrailingSlashes(target.pathname);
1993
2222
  const newSuffix = suffix && suffix === connectionPath ? targetPath : suffix;
1994
2223
  return `${target.origin}/.well-known/${doc}${newSuffix}${requested.search}`;
1995
2224
  } catch {
@@ -2140,6 +2369,7 @@ var BrowserOAuthClientProvider = class {
2140
2369
  return this.session.tokens(ctx);
2141
2370
  }
2142
2371
  saveTokens(tokens, ctx) {
2372
+ this.lastAttemptedAuthUrl = null;
2143
2373
  return this.session.saveTokens(tokens, ctx);
2144
2374
  }
2145
2375
  async clientInformation(ctx) {
@@ -2206,16 +2436,21 @@ var BrowserOAuthClientProvider = class {
2206
2436
  * use `redirectToAuthorization` for that.
2207
2437
  */
2208
2438
  async prepareAuthorizationUrl(authorizationUrl) {
2209
- return this.session.storeAuthorizationState(authorizationUrl, {
2210
- extraProviderOptions: {
2211
- oauthProxyUrl: this.oauthProxyUrl,
2212
- ...this.clientMetadataUrl ? { clientMetadataUrl: this.clientMetadataUrl } : {},
2213
- ...this.staticClientInfo ? { staticClientInfo: this.staticClientInfo } : {},
2214
- ...this.scope ? { scope: this.scope } : {}
2215
- },
2216
- flowType: this.useRedirectFlow ? "redirect" : "popup",
2217
- returnUrl: typeof window !== "undefined" ? window.location.href : void 0
2218
- });
2439
+ const prepared = await this.session.storeAuthorizationState(
2440
+ authorizationUrl,
2441
+ {
2442
+ extraProviderOptions: {
2443
+ oauthProxyUrl: this.oauthProxyUrl,
2444
+ ...this.clientMetadataUrl ? { clientMetadataUrl: this.clientMetadataUrl } : {},
2445
+ ...this.staticClientInfo ? { staticClientInfo: this.staticClientInfo } : {},
2446
+ ...this.scope ? { scope: this.scope } : {}
2447
+ },
2448
+ flowType: this.useRedirectFlow ? "redirect" : "popup",
2449
+ returnUrl: typeof window !== "undefined" ? window.location.href : void 0
2450
+ }
2451
+ );
2452
+ this.lastAttemptedAuthUrl = prepared;
2453
+ return prepared;
2219
2454
  }
2220
2455
  /**
2221
2456
  * Redirects the user agent to the authorization URL, storing necessary state.
@@ -2267,64 +2502,24 @@ var BrowserOAuthClientProvider = class {
2267
2502
  * Retrieves the last URL passed to `redirectToAuthorization`. Useful for manual fallback.
2268
2503
  */
2269
2504
  getLastAttemptedAuthUrl() {
2270
- const storedUrl = localStorage.getItem(this.getKey("last_auth_url"));
2271
- if (!storedUrl) return null;
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;
2505
+ return this.lastAttemptedAuthUrl;
2297
2506
  }
2298
2507
  clearStorage() {
2508
+ this.lastAttemptedAuthUrl = null;
2299
2509
  const prefixPattern = `${this.storageKeyPrefix}_${this.serverUrlHash}_`;
2300
2510
  const statePattern = `${this.storageKeyPrefix}:state_`;
2301
2511
  const keysToRemove = [];
2302
2512
  let count = 0;
2303
- for (let i = 0; i < localStorage.length; i++) {
2304
- const key = localStorage.key(i);
2305
- if (!key) continue;
2513
+ for (const key of this.storage.keys()) {
2306
2514
  if (key.startsWith(prefixPattern)) {
2307
2515
  keysToRemove.push(key);
2308
2516
  } else if (key.startsWith(statePattern)) {
2309
- try {
2310
- const item = localStorage.getItem(key);
2311
- if (item) {
2312
- const state = JSON.parse(item);
2313
- if (state.serverUrlHash === this.serverUrlHash) {
2314
- keysToRemove.push(key);
2315
- }
2316
- }
2317
- } catch (e) {
2318
- console.warn(
2319
- `[${this.storageKeyPrefix}] Error parsing state key ${key} during clearStorage:`,
2320
- e
2321
- );
2322
- }
2517
+ keysToRemove.push(key);
2323
2518
  }
2324
2519
  }
2325
2520
  const uniqueKeysToRemove = [...new Set(keysToRemove)];
2326
2521
  uniqueKeysToRemove.forEach((key) => {
2327
- localStorage.removeItem(key);
2522
+ this.storage.remove(key);
2328
2523
  count++;
2329
2524
  });
2330
2525
  return count;
@@ -5915,45 +6110,6 @@ import React, {
5915
6110
  useState as useState3
5916
6111
  } from "react";
5917
6112
 
5918
- // src/react/types.ts
5919
- var PERSISTED_SERVER_CONFIG_KEYS = [
5920
- "url",
5921
- "displayName",
5922
- "enabled",
5923
- "proxyConfig",
5924
- "oauthProxyUrl",
5925
- "connectionMode",
5926
- "autoProxyFallback",
5927
- "callbackUrl",
5928
- "storageKeyPrefix",
5929
- "headers",
5930
- "logLevel",
5931
- "autoRetry",
5932
- "autoReconnect",
5933
- "reconnectionOptions",
5934
- "popupFeatures",
5935
- "preventAutoAuth",
5936
- "useRedirectFlow",
5937
- "clientOptions",
5938
- "protocolNegotiation",
5939
- "timeout",
5940
- "clientInfo",
5941
- "oauth"
5942
- ];
5943
- function pickPersistedServerConfig(source) {
5944
- const out = {};
5945
- for (const key of PERSISTED_SERVER_CONFIG_KEYS) {
5946
- const value = source[key];
5947
- if (value !== void 0) {
5948
- out[key] = value;
5949
- }
5950
- }
5951
- return out;
5952
- }
5953
- function toPersistedServerConfig(config) {
5954
- return pickPersistedServerConfig(config);
5955
- }
5956
-
5957
6113
  // src/react/useMcpServerQueues.ts
5958
6114
  import { useCallback as useCallback3, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
5959
6115
  var MAX_NOTIFICATIONS = 500;
@@ -6140,8 +6296,8 @@ function sameSerializedValue(left, right) {
6140
6296
  }
6141
6297
  function isSameMcpServer(left, right) {
6142
6298
  return left.id === right.id && sameSerializedValue(
6143
- pickPersistedServerConfig(left),
6144
- pickPersistedServerConfig(right)
6299
+ pickLiveServerConfig(left),
6300
+ pickLiveServerConfig(right)
6145
6301
  ) && left.name === right.name && left.state === right.state && left.error === right.error && left.authUrl === right.authUrl && sameSerializedValue(left.authTokens, right.authTokens) && left.protocolEra === right.protocolEra && left.protocolVersion === right.protocolVersion && sameSerializedValue(left.serverInfo, right.serverInfo) && sameSerializedValue(left.capabilities, right.capabilities) && left.instructions === right.instructions && sameSerializedValue(left.extensions, right.extensions) && sameSerializedValue(left.tools, right.tools) && sameSerializedValue(left.resources, right.resources) && sameSerializedValue(left.resourceTemplates, right.resourceTemplates) && sameSerializedValue(left.prompts, right.prompts) && sameSerializedValue(left.notifications, right.notifications) && left.unreadNotificationCount === right.unreadNotificationCount && sameSerializedValue(
6146
6302
  left.pendingSamplingRequests,
6147
6303
  right.pendingSamplingRequests
@@ -6275,7 +6431,7 @@ function McpServerWrapper({
6275
6431
  }, [onUpdate]);
6276
6432
  useEffect3(() => {
6277
6433
  const server = {
6278
- ...toPersistedServerConfig(options),
6434
+ ...pickLiveServerConfig(options),
6279
6435
  ...mcp,
6280
6436
  id,
6281
6437
  displayName: displayName || options.displayName || id,
@@ -6622,8 +6778,8 @@ function McpClientProvider({
6622
6778
  ...options
6623
6779
  };
6624
6780
  if (sameSerializedValue(
6625
- pickPersistedServerConfig(currentConfig.options),
6626
- pickPersistedServerConfig(updatedOptions)
6781
+ pickLiveServerConfig(currentConfig.options),
6782
+ pickLiveServerConfig(updatedOptions)
6627
6783
  )) {
6628
6784
  return;
6629
6785
  }
@@ -6790,17 +6946,42 @@ var LocalStorageProvider = class {
6790
6946
  getServers() {
6791
6947
  try {
6792
6948
  const stored = localStorage.getItem(this.storageKey);
6793
- return stored ? JSON.parse(stored) : {};
6794
- } catch (error) {
6795
- console.error("[LocalStorageProvider] Failed to load servers:", error);
6949
+ if (!stored) return {};
6950
+ const parsed = JSON.parse(stored);
6951
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
6952
+ return {};
6953
+ }
6954
+ const sanitized = Object.fromEntries(
6955
+ Object.entries(parsed).flatMap(
6956
+ ([id, config]) => config && typeof config === "object" && !Array.isArray(config) ? [
6957
+ [
6958
+ id,
6959
+ toPersistedServerConfig(config)
6960
+ ]
6961
+ ] : []
6962
+ )
6963
+ );
6964
+ const serialized = JSON.stringify(sanitized);
6965
+ if (serialized !== stored) {
6966
+ localStorage.setItem(this.storageKey, serialized);
6967
+ }
6968
+ return sanitized;
6969
+ } catch {
6970
+ console.error("[LocalStorageProvider] Failed to load servers.");
6796
6971
  return {};
6797
6972
  }
6798
6973
  }
6799
6974
  setServers(servers) {
6800
6975
  try {
6801
- localStorage.setItem(this.storageKey, JSON.stringify(servers));
6802
- } catch (error) {
6803
- console.error("[LocalStorageProvider] Failed to save servers:", error);
6976
+ const sanitized = Object.fromEntries(
6977
+ Object.entries(servers).map(([id, config]) => [
6978
+ id,
6979
+ toPersistedServerConfig(config)
6980
+ ])
6981
+ );
6982
+ localStorage.setItem(this.storageKey, JSON.stringify(sanitized));
6983
+ } catch {
6984
+ console.error("[LocalStorageProvider] Failed to save servers.");
6804
6985
  }
6805
6986
  }
6806
6987
  setServer(id, config) {
@@ -6818,24 +6999,24 @@ var LocalStorageProvider = class {
6818
6999
  try {
6819
7000
  localStorage.removeItem(this.storageKey);
6820
7001
  localStorage.removeItem(this.metadataKey);
6821
- } catch (error) {
6822
- console.error("[LocalStorageProvider] Failed to clear:", error);
7002
+ } catch {
7003
+ console.error("[LocalStorageProvider] Failed to clear.");
6823
7004
  }
6824
7005
  }
6825
7006
  getAllMetadata() {
6826
7007
  try {
6827
7008
  const stored = localStorage.getItem(this.metadataKey);
6828
7009
  return stored ? JSON.parse(stored) : {};
6829
- } catch (error) {
6830
- console.error("[LocalStorageProvider] Failed to load metadata:", error);
7010
+ } catch {
7011
+ console.error("[LocalStorageProvider] Failed to load metadata.");
6831
7012
  return {};
6832
7013
  }
6833
7014
  }
6834
7015
  setAllMetadata(metadata) {
6835
7016
  try {
6836
7017
  localStorage.setItem(this.metadataKey, JSON.stringify(metadata));
6837
- } catch (error) {
6838
- console.error("[LocalStorageProvider] Failed to save metadata:", error);
7018
+ } catch {
7019
+ console.error("[LocalStorageProvider] Failed to save metadata.");
6839
7020
  }
6840
7021
  }
6841
7022
  getServerMetadata(id) {
@@ -6859,10 +7040,15 @@ var MemoryStorageProvider = class {
6859
7040
  return { ...this.storage };
6860
7041
  }
6861
7042
  setServers(servers) {
6862
- this.storage = { ...servers };
7043
+ this.storage = Object.fromEntries(
7044
+ Object.entries(servers).map(([id, config]) => [
7045
+ id,
7046
+ toPersistedServerConfig(config)
7047
+ ])
7048
+ );
6863
7049
  }
6864
7050
  setServer(id, config) {
6865
- this.storage[id] = config;
7051
+ this.storage[id] = toPersistedServerConfig(config);
6866
7052
  }
6867
7053
  removeServer(id) {
6868
7054
  delete this.storage[id];
@@ -6942,32 +7128,46 @@ var OPENAI_FILE_APIS_SCRIPT = `<script>
6942
7128
  })();
6943
7129
  </script>`;
6944
7130
  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>"
7131
+ const headEnd = findOpeningConstructEnd(html, "<head");
7132
+ if (headEnd !== void 0) {
7133
+ return insertAt(html, headEnd, OPENAI_FILE_APIS_SCRIPT);
7134
+ }
7135
+ const htmlEnd = findOpeningConstructEnd(html, "<html");
7136
+ if (htmlEnd !== void 0) {
7137
+ return insertAt(
7138
+ html,
7139
+ htmlEnd,
7140
+ "<head>" + OPENAI_FILE_APIS_SCRIPT + "</head>"
6955
7141
  );
6956
7142
  }
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>"
7143
+ const doctypeEnd = findOpeningConstructEnd(html, "<!doctype");
7144
+ if (doctypeEnd !== void 0) {
7145
+ return insertAt(
7146
+ html,
7147
+ doctypeEnd,
7148
+ "<head>" + OPENAI_FILE_APIS_SCRIPT + "</head>"
6967
7149
  );
6968
7150
  }
6969
7151
  return OPENAI_FILE_APIS_SCRIPT + html;
6970
7152
  }
7153
+ function findOpeningConstructEnd(html, lowercasePrefix) {
7154
+ const lowercaseHtml = html.toLowerCase();
7155
+ let searchFrom = 0;
7156
+ while (searchFrom < lowercaseHtml.length) {
7157
+ const start = lowercaseHtml.indexOf(lowercasePrefix, searchFrom);
7158
+ if (start === -1) return void 0;
7159
+ const boundary = lowercaseHtml[start + lowercasePrefix.length];
7160
+ if (boundary === ">" || boundary === " " || boundary === " " || boundary === "\n" || boundary === "\r" || boundary === "\f") {
7161
+ const end = lowercaseHtml.indexOf(">", start + lowercasePrefix.length);
7162
+ return end === -1 ? void 0 : end + 1;
7163
+ }
7164
+ searchFrom = start + lowercasePrefix.length;
7165
+ }
7166
+ return void 0;
7167
+ }
7168
+ function insertAt(value, index, addition) {
7169
+ return value.slice(0, index) + addition + value.slice(index);
7170
+ }
6971
7171
 
6972
7172
  // src/react/view/resolve-view-resource.ts
6973
7173
  function resolveViewResource(options) {
@@ -8098,10 +8298,12 @@ export {
8098
8298
  isViewTool,
8099
8299
  onMcpAuthorization,
8100
8300
  parseCustomProps,
8301
+ pickPersistedServerConfig,
8101
8302
  resolveViewResource,
8102
8303
  setTelemetrySource,
8103
8304
  specTypeSchemas,
8104
8305
  subscribeToRpcLogs,
8306
+ toPersistedServerConfig,
8105
8307
  useMcp,
8106
8308
  useMcpClient,
8107
8309
  useMcpServer