From e2f33d94bdfdfba22d92940999458ba27dbb9253 Mon Sep 17 00:00:00 2001 From: Matthew Wang Date: Fri, 25 Jul 2025 13:21:22 -0700 Subject: [PATCH 1/2] Cache tool call params --- client/src/App.tsx | 8 ++++ client/src/components/ToolsTab.tsx | 31 ++++++++++--- client/src/utils/toolCache.ts | 71 ++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 client/src/utils/toolCache.ts diff --git a/client/src/App.tsx b/client/src/App.tsx index c4c89564b..962529d52 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -22,6 +22,7 @@ import { SESSION_KEYS, getServerSpecificKey } from "./lib/constants"; import { AuthDebuggerState, EMPTY_DEBUGGER_STATE } from "./lib/auth-types"; import { OAuthStateMachine } from "./lib/oauth-state-machine"; import { cacheToolOutputSchemas } from "./utils/schemaUtils"; +import { saveToolParamsForCache } from "./utils/toolCache"; import React, { Suspense, useCallback, @@ -688,6 +689,12 @@ const App = () => { const callTool = async (name: string, params: Record) => { lastToolCallOriginTabRef.current = currentTabRef.current; + // Save tool parameters to cache before making the call + const tool = tools.find((t) => t.name === name); + if (tool && sseUrl) { + saveToolParamsForCache(sseUrl, name, tool, params); + } + try { const response = await sendMCPRequest( { @@ -1020,6 +1027,7 @@ const App = () => { clearError("resources"); readResource(uri); }} + serverUrl={sseUrl} /> void; @@ -42,24 +44,43 @@ const ToolsTab = ({ error: string | null; resourceContent: Record; onReadResource?: (uri: string) => void; + serverUrl: string; }) => { const [params, setParams] = useState>({}); const [isToolRunning, setIsToolRunning] = useState(false); const [isOutputSchemaExpanded, setIsOutputSchemaExpanded] = useState(false); useEffect(() => { - const params = Object.entries( - selectedTool?.inputSchema.properties ?? [], + if (!selectedTool) { + setParams({}); + return; + } + + // Generate default parameters from schema + const defaultParams = Object.entries( + selectedTool.inputSchema.properties ?? [], ).map(([key, value]) => [ key, generateDefaultValue( value as JsonSchemaType, key, - selectedTool?.inputSchema as JsonSchemaType, + selectedTool.inputSchema as JsonSchemaType, ), ]); - setParams(Object.fromEntries(params)); - }, [selectedTool]); + const defaultParamsObj = Object.fromEntries(defaultParams); + + // Fetch cached params from localStorage if they exist + const cachedParams = loadToolParamsFromCache( + serverUrl, + selectedTool.name, + selectedTool, + ); + + // Merge cached params with defaults, giving preference to cached values + const mergedParams = { ...defaultParamsObj, ...cachedParams }; + + setParams(mergedParams); + }, [selectedTool, serverUrl]); return ( diff --git a/client/src/utils/toolCache.ts b/client/src/utils/toolCache.ts new file mode 100644 index 000000000..82718c46e --- /dev/null +++ b/client/src/utils/toolCache.ts @@ -0,0 +1,71 @@ +import { Tool } from "@modelcontextprotocol/sdk/types.js"; + +export interface CacheKey { + serverUrl: string; + toolName: string; + paramNames: string[]; +} + +export function generateCacheKey( + serverUrl: string, + toolName: string, + tool: Tool, +): string { + const paramNames = Object.keys(tool.inputSchema.properties ?? {}).sort(); + const key = `tool_params_${btoa(`${serverUrl}_${toolName}_${JSON.stringify(paramNames)}`)}`; + return key; +} + +export function saveToolParamsForCache( + serverUrl: string, + toolName: string, + tool: Tool, + params: Record, +): void { + try { + const cacheKey = generateCacheKey(serverUrl, toolName, tool); + const cacheData = { + params, + timestamp: Date.now(), + toolName, + serverUrl, + }; + localStorage.setItem(cacheKey, JSON.stringify(cacheData)); + } catch (error) { + console.warn("Failed to save tool parameters to cache:", error); + } +} + +export function loadToolParamsFromCache( + serverUrl: string, + toolName: string, + tool: Tool, +): Record | null { + try { + const cacheKey = generateCacheKey(serverUrl, toolName, tool); + const cached = localStorage.getItem(cacheKey); + + if (!cached) { + return null; + } + + const cacheData = JSON.parse(cached); + + // Validate cache data structure + if (!cacheData.params || !cacheData.timestamp) { + return null; + } + + // Optional: Check if cache is too old (e.g., 30 days) + const maxAge = 30 * 24 * 60 * 60 * 1000; // 30 days in milliseconds + if (Date.now() - cacheData.timestamp > maxAge) { + localStorage.removeItem(cacheKey); + return null; + } + + return cacheData.params; + } catch (error) { + console.warn("Failed to load tool parameters from cache:", error); + return null; + } +} From da069bf0ff67910367d9aaec9f495c4875674ed1 Mon Sep 17 00:00:00 2001 From: Matthew Wang Date: Fri, 25 Jul 2025 13:34:32 -0700 Subject: [PATCH 2/2] remove the aged logic --- client/src/utils/toolCache.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/client/src/utils/toolCache.ts b/client/src/utils/toolCache.ts index 82718c46e..4c34b7632 100644 --- a/client/src/utils/toolCache.ts +++ b/client/src/utils/toolCache.ts @@ -51,15 +51,7 @@ export function loadToolParamsFromCache( const cacheData = JSON.parse(cached); - // Validate cache data structure - if (!cacheData.params || !cacheData.timestamp) { - return null; - } - - // Optional: Check if cache is too old (e.g., 30 days) - const maxAge = 30 * 24 * 60 * 60 * 1000; // 30 days in milliseconds - if (Date.now() - cacheData.timestamp > maxAge) { - localStorage.removeItem(cacheKey); + if (!cacheData.params) { return null; }