-
Notifications
You must be signed in to change notification settings - Fork 744
Cache tool call params #646
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ import { useEffect, useState } from "react"; | |
import ListPane from "./ListPane"; | ||
import JsonView from "./JsonView"; | ||
import ToolResults from "./ToolResults"; | ||
import { loadToolParamsFromCache } from "@/utils/toolCache"; | ||
|
||
// Type guard to safely detect the optional _meta field without using `any` | ||
const hasMeta = (tool: Tool): tool is Tool & { _meta: unknown } => | ||
|
@@ -34,6 +35,7 @@ const ToolsTab = ({ | |
nextCursor, | ||
resourceContent, | ||
onReadResource, | ||
serverUrl, | ||
}: { | ||
tools: Tool[]; | ||
listTools: () => void; | ||
|
@@ -46,25 +48,44 @@ const ToolsTab = ({ | |
error: string | null; | ||
resourceContent: Record<string, string>; | ||
onReadResource?: (uri: string) => void; | ||
serverUrl: string; | ||
}) => { | ||
const [params, setParams] = useState<Record<string, unknown>>({}); | ||
const [isToolRunning, setIsToolRunning] = useState(false); | ||
const [isOutputSchemaExpanded, setIsOutputSchemaExpanded] = useState(false); | ||
const [isMetaExpanded, setIsMetaExpanded] = 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 }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. First load the empty default params, then load the cached ones if they exist. |
||
|
||
setParams(mergedParams); | ||
}, [selectedTool, serverUrl]); | ||
|
||
return ( | ||
<TabsContent value="tools"> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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)}`)}`; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This ensures uniqueness in localStorage |
||
return key; | ||
} | ||
|
||
export function saveToolParamsForCache( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We create a tool cache helper util that helps save and load things from |
||
serverUrl: string, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can probably remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, I can see advantages either way. Probably safer to start conservative, with the additional uniqueness of the server URL? |
||
toolName: string, | ||
tool: Tool, | ||
params: Record<string, unknown>, | ||
): 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<string, unknown> | 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; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Save the tool parameters to localStorage cache on execution.