diff --git a/client/src/App.tsx b/client/src/App.tsx index 5937d738..02e20994 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 { cleanParams } from "./utils/paramUtils"; import type { JsonSchemaType } from "./utils/jsonUtils"; import React, { @@ -787,6 +788,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 { // Find the tool schema to clean parameters properly const tool = tools.find((t) => t.name === name); @@ -1129,6 +1136,7 @@ const App = () => { clearError("resources"); readResource(uri); }} + serverUrl={sseUrl} /> void; @@ -68,6 +70,7 @@ const ToolsTab = ({ error: string | null; resourceContent: Record; onReadResource?: (uri: string) => void; + serverUrl: string; }) => { const [params, setParams] = useState>({}); const [isToolRunning, setIsToolRunning] = useState(false); @@ -88,24 +91,42 @@ const ToolsTab = ({ }; 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)); + 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); // Reset validation errors when switching tools setHasValidationErrors(false); // Clear form refs for the previous tool formRefs.current = {}; - }, [selectedTool]); + }, [selectedTool, serverUrl]); return ( diff --git a/client/src/components/__tests__/ToolsTab.test.tsx b/client/src/components/__tests__/ToolsTab.test.tsx index d9237be2..42cfeca3 100644 --- a/client/src/components/__tests__/ToolsTab.test.tsx +++ b/client/src/components/__tests__/ToolsTab.test.tsx @@ -68,6 +68,7 @@ describe("ToolsTab", () => { error: null, resourceContent: {}, onReadResource: jest.fn(), + serverUrl: "http://localhost:3000", }; const renderToolsTab = (props = {}) => { diff --git a/client/src/utils/toolCache.ts b/client/src/utils/toolCache.ts new file mode 100644 index 00000000..4c34b763 --- /dev/null +++ b/client/src/utils/toolCache.ts @@ -0,0 +1,63 @@ +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); + + if (!cacheData.params) { + return null; + } + + return cacheData.params; + } catch (error) { + console.warn("Failed to load tool parameters from cache:", error); + return null; + } +}