From 841c42e4688ee4af8a3bf180971d856167fda11c Mon Sep 17 00:00:00 2001 From: olaservo Date: Wed, 18 Feb 2026 21:06:44 -0700 Subject: [PATCH 1/2] Remove misleading structured content compatibility warning The MCP spec allows `content` to be a human-readable summary rather than a JSON-stringified copy of `structuredContent`. Remove the yellow warning that was shown when no text block JSON-matched structuredContent, keeping only the positive blue indicator when a match does exist. Fixes #1089 Co-Authored-By: Claude Opus 4.6 --- client/src/components/ToolResults.tsx | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/client/src/components/ToolResults.tsx b/client/src/components/ToolResults.tsx index 38d1d0382..d036298b9 100644 --- a/client/src/components/ToolResults.tsx +++ b/client/src/components/ToolResults.tsx @@ -22,17 +22,14 @@ const checkContentCompatibility = ( text?: string; [key: string]: unknown; }>, -): { isCompatible: boolean; message: string } => { +): { hasMatch: boolean; message: string } | null => { // Look for at least one text content block that matches the structured content const textBlocks = unstructuredContent.filter( (block) => block.type === "text", ); if (textBlocks.length === 0) { - return { - isCompatible: false, - message: "No text blocks to match structured content", - }; + return null; } // Check if any text block contains JSON that matches the structured content @@ -49,7 +46,7 @@ const checkContentCompatibility = ( if (isEqual) { return { - isCompatible: true, + hasMatch: true, message: `Structured content matches text block${textBlocks.length > 1 ? " (multiple blocks)" : ""}${unstructuredContent.length > textBlocks.length ? " + other content" : ""}`, }; } @@ -59,10 +56,7 @@ const checkContentCompatibility = ( } } - return { - isCompatible: false, - message: "No text block matches structured content", - }; + return null; }; const ToolResults = ({ @@ -195,16 +189,9 @@ const ToolResults = ({
Unstructured Content:
- {compatibilityResult && ( -
- {compatibilityResult.isCompatible ? "✓" : "⚠"}{" "} - {compatibilityResult.message} + {compatibilityResult?.hasMatch && ( +
+ ✓ {compatibilityResult.message}
)} From 0a1330d46405a15b6c62ede5afea427f88189397 Mon Sep 17 00:00:00 2001 From: olaservo Date: Thu, 19 Feb 2026 07:37:44 -0700 Subject: [PATCH 2/2] Fix stale tool parameters sent on Run Tool click (#988) The Run Tool handler read params from React state immediately after calling validateJson(), but setState is async so the values were stale. - Have validateJson() return the parsed value so the caller can use it directly without waiting for React's next render cycle - Rewrite checkValidationErrors to collect fresh values from each form ref and merge them into params before calling callTool - Convert all setParams({...params}) calls to use the functional updater form setParams(prev => ({...prev})) to prevent stale closures when rapidly editing multiple fields - Add regression test that verifies fresh values are sent even within the 300ms debounce window Fixes modelcontextprotocol/inspector#988 Co-Authored-By: Claude Opus 4.6 --- client/src/components/DynamicJsonForm.tsx | 8 +- client/src/components/ToolsTab.tsx | 108 +++++++++++------- .../components/__tests__/ToolsTab.test.tsx | 94 +++++++++++++++ 3 files changed, 165 insertions(+), 45 deletions(-) diff --git a/client/src/components/DynamicJsonForm.tsx b/client/src/components/DynamicJsonForm.tsx index ecd150f22..8ef722cd1 100644 --- a/client/src/components/DynamicJsonForm.tsx +++ b/client/src/components/DynamicJsonForm.tsx @@ -27,7 +27,11 @@ interface DynamicJsonFormProps { } export interface DynamicJsonFormRef { - validateJson: () => { isValid: boolean; error: string | null }; + validateJson: () => { + isValid: boolean; + error: string | null; + value?: JsonValue; + }; hasJsonError: () => boolean; } @@ -222,7 +226,7 @@ const DynamicJsonForm = forwardRef( } onChange(parsed); setJsonError(undefined); - return { isValid: true, error: null }; + return { isValid: true, error: null, value: parsed }; } catch (err) { const errorMessage = err instanceof Error ? err.message : "Invalid JSON"; diff --git a/client/src/components/ToolsTab.tsx b/client/src/components/ToolsTab.tsx index 339ba7c61..afc4e66b9 100644 --- a/client/src/components/ToolsTab.tsx +++ b/client/src/components/ToolsTab.tsx @@ -181,14 +181,30 @@ const ToolsTab = ({ const { copied, setCopied } = useCopy(); // Function to check if any form has validation errors + // When validateChildren is true, also collects fresh parsed values from JSON editors const checkValidationErrors = (validateChildren: boolean = false) => { - const errors = Object.values(formRefs.current).some( - (ref) => - ref && - (validateChildren ? !ref.validateJson().isValid : ref.hasJsonError()), - ); - setHasValidationErrors(errors); - return errors; + const freshValues: Record = {}; + let hasErrors = false; + + for (const [key, ref] of Object.entries(formRefs.current)) { + if (!ref) continue; + if (validateChildren) { + const result = ref.validateJson(); + if (!result.isValid) { + hasErrors = true; + } + if (result.value !== undefined) { + freshValues[key] = result.value; + } + } else { + if (ref.hasJsonError()) { + hasErrors = true; + } + } + } + + setHasValidationErrors(hasErrors); + return { hasErrors, freshValues }; }; useEffect(() => { @@ -333,8 +349,8 @@ const ToolsTab = ({ name={key} checked={params[key] === null} onCheckedChange={(checked: boolean) => - setParams({ - ...params, + setParams((prev) => ({ + ...prev, [key]: checked ? null : prop.type === "array" @@ -349,7 +365,7 @@ const ToolsTab = ({ prop.type === "integer" ? undefined : undefined, - }) + })) } />