From ab062d8f9d8f4f6deb6a7ecd72df07b38b7d3afc Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Wed, 11 Jun 2025 11:58:21 +0100 Subject: [PATCH] refactor: implement split storage for inspector config - Add is_session_item field to ConfigItem type to control storage location - Store auth tokens in sessionStorage (cleared on browser close) - Store other settings in localStorage (persist between sessions) - Maintain backward compatibility with existing config structure This provides better security for auth tokens while preserving user preferences across browser sessions. --- client/src/App.tsx | 3 +- client/src/lib/configurationTypes.ts | 1 + client/src/lib/constants.ts | 5 ++ client/src/utils/configUtils.ts | 76 +++++++++++++++++++++------- 4 files changed, 66 insertions(+), 19 deletions(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index d264a8fa..c44a0e5f 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -68,6 +68,7 @@ import { getInitialCommand, getInitialArgs, initializeInspectorConfig, + saveInspectorConfig, } from "./utils/configUtils"; const CONFIG_LOCAL_STORAGE_KEY = "inspectorConfig_v1"; @@ -226,7 +227,7 @@ const App = () => { }, [headerName]); useEffect(() => { - localStorage.setItem(CONFIG_LOCAL_STORAGE_KEY, JSON.stringify(config)); + saveInspectorConfig(CONFIG_LOCAL_STORAGE_KEY, config); }, [config]); // Auto-connect to previously saved serverURL after OAuth callback diff --git a/client/src/lib/configurationTypes.ts b/client/src/lib/configurationTypes.ts index c2024e51..263b2cf2 100644 --- a/client/src/lib/configurationTypes.ts +++ b/client/src/lib/configurationTypes.ts @@ -2,6 +2,7 @@ export type ConfigItem = { label: string; description: string; value: string | number | boolean; + is_session_item: boolean; }; /** diff --git a/client/src/lib/constants.ts b/client/src/lib/constants.ts index d1ca972a..922f1943 100644 --- a/client/src/lib/constants.ts +++ b/client/src/lib/constants.ts @@ -36,28 +36,33 @@ export const DEFAULT_INSPECTOR_CONFIG: InspectorConfig = { label: "Request Timeout", description: "Timeout for requests to the MCP server (ms)", value: 10000, + is_session_item: false, }, MCP_REQUEST_TIMEOUT_RESET_ON_PROGRESS: { label: "Reset Timeout on Progress", description: "Reset timeout on progress notifications", value: true, + is_session_item: false, }, MCP_REQUEST_MAX_TOTAL_TIMEOUT: { label: "Maximum Total Timeout", description: "Maximum total timeout for requests sent to the MCP server (ms) (Use with progress notifications)", value: 60000, + is_session_item: false, }, MCP_PROXY_FULL_ADDRESS: { label: "Inspector Proxy Address", description: "Set this if you are running the MCP Inspector Proxy on a non-default address. Example: http://10.1.1.22:5577", value: "", + is_session_item: false, }, MCP_PROXY_AUTH_TOKEN: { label: "Proxy Session Token", description: "Session token for authenticating with the MCP Proxy Server (displayed in proxy console on startup)", value: "", + is_session_item: true, }, } as const; diff --git a/client/src/utils/configUtils.ts b/client/src/utils/configUtils.ts index 55b29a17..3e4d3e9b 100644 --- a/client/src/utils/configUtils.ts +++ b/client/src/utils/configUtils.ts @@ -104,27 +104,67 @@ export const getConfigOverridesFromQueryParams = ( export const initializeInspectorConfig = ( localStorageKey: string, ): InspectorConfig => { - const savedConfig = localStorage.getItem(localStorageKey); - let baseConfig: InspectorConfig; - if (savedConfig) { - // merge default config with saved config - const mergedConfig = { - ...DEFAULT_INSPECTOR_CONFIG, - ...JSON.parse(savedConfig), - } as InspectorConfig; + // Read persistent config from localStorage + const savedPersistentConfig = localStorage.getItem(localStorageKey); + // Read ephemeral config from sessionStorage + const savedEphemeralConfig = sessionStorage.getItem( + `${localStorageKey}_ephemeral`, + ); - // update description of keys to match the new description (in case of any updates to the default config description) - for (const [key, value] of Object.entries(mergedConfig)) { - mergedConfig[key as keyof InspectorConfig] = { - ...value, - label: DEFAULT_INSPECTOR_CONFIG[key as keyof InspectorConfig].label, - }; - } - baseConfig = mergedConfig; - } else { - baseConfig = DEFAULT_INSPECTOR_CONFIG; + // Start with default config + let baseConfig = { ...DEFAULT_INSPECTOR_CONFIG }; + + // Apply saved persistent config + if (savedPersistentConfig) { + const parsedPersistentConfig = JSON.parse(savedPersistentConfig); + baseConfig = { ...baseConfig, ...parsedPersistentConfig }; } + + // Apply saved ephemeral config + if (savedEphemeralConfig) { + const parsedEphemeralConfig = JSON.parse(savedEphemeralConfig); + baseConfig = { ...baseConfig, ...parsedEphemeralConfig }; + } + + // Ensure all config items have the latest labels/descriptions from defaults + for (const [key, value] of Object.entries(baseConfig)) { + baseConfig[key as keyof InspectorConfig] = { + ...value, + label: DEFAULT_INSPECTOR_CONFIG[key as keyof InspectorConfig].label, + description: + DEFAULT_INSPECTOR_CONFIG[key as keyof InspectorConfig].description, + is_session_item: + DEFAULT_INSPECTOR_CONFIG[key as keyof InspectorConfig].is_session_item, + }; + } + // Apply query param overrides const overrides = getConfigOverridesFromQueryParams(DEFAULT_INSPECTOR_CONFIG); return { ...baseConfig, ...overrides }; }; + +export const saveInspectorConfig = ( + localStorageKey: string, + config: InspectorConfig, +): void => { + const persistentConfig: Partial = {}; + const ephemeralConfig: Partial = {}; + + // Split config based on is_session_item flag + for (const [key, value] of Object.entries(config)) { + if (value.is_session_item) { + ephemeralConfig[key as keyof InspectorConfig] = value; + } else { + persistentConfig[key as keyof InspectorConfig] = value; + } + } + + // Save persistent config to localStorage + localStorage.setItem(localStorageKey, JSON.stringify(persistentConfig)); + + // Save ephemeral config to sessionStorage + sessionStorage.setItem( + `${localStorageKey}_ephemeral`, + JSON.stringify(ephemeralConfig), + ); +};