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.
This commit is contained in:
parent
32e6bdd13c
commit
ab062d8f9d
4 changed files with 67 additions and 20 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ export type ConfigItem = {
|
|||
label: string;
|
||||
description: string;
|
||||
value: string | number | boolean;
|
||||
is_session_item: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<InspectorConfig> = {};
|
||||
const ephemeralConfig: Partial<InspectorConfig> = {};
|
||||
|
||||
// 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),
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue