Add real-time JSON validation and prevent tool execution on invalid input

This commit is contained in:
Nandha Reddy 2025-09-24 00:25:06 +10:00
commit 8d149b2673
2 changed files with 39 additions and 8 deletions

View file

@ -106,8 +106,17 @@ const DynamicJsonForm = forwardRef<DynamicJsonFormRef, DynamicJsonFormProps>(
const parsed = JSON.parse(jsonString); const parsed = JSON.parse(jsonString);
onChange(parsed); onChange(parsed);
setJsonError(undefined); setJsonError(undefined);
} catch { } catch (err) {
// Don't set error during normal typing // For invalid JSON, set error and reset to default if it's clearly malformed
const errorMessage =
err instanceof Error ? err.message : "Invalid JSON";
setJsonError(errorMessage);
// Reset to default for clearly invalid JSON (not just incomplete typing)
const trimmed = jsonString.trim();
if (trimmed.length > 5 && !trimmed.match(/^[\s\[{]/)) {
onChange(generateDefaultValue(schema));
}
} }
}, 300); }, 300);
}, },
@ -164,7 +173,12 @@ const DynamicJsonForm = forwardRef<DynamicJsonFormRef, DynamicJsonFormProps>(
try { try {
const jsonStr = rawJsonValue.trim(); const jsonStr = rawJsonValue.trim();
if (!jsonStr) return { isValid: true, error: null }; if (!jsonStr) return { isValid: true, error: null };
JSON.parse(jsonStr); const parsed = JSON.parse(jsonStr);
// Clear any pending debounced update and immediately update parent
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
onChange(parsed);
setJsonError(undefined); setJsonError(undefined);
return { isValid: true, error: null }; return { isValid: true, error: null };
} catch (err) { } catch (err) {

View file

@ -66,10 +66,20 @@ const ToolsTab = ({
const [isToolRunning, setIsToolRunning] = useState(false); const [isToolRunning, setIsToolRunning] = useState(false);
const [isOutputSchemaExpanded, setIsOutputSchemaExpanded] = useState(false); const [isOutputSchemaExpanded, setIsOutputSchemaExpanded] = useState(false);
const [isMetaExpanded, setIsMetaExpanded] = useState(false); const [isMetaExpanded, setIsMetaExpanded] = useState(false);
const [hasValidationErrors, setHasValidationErrors] = useState(false);
const formRefs = useRef<Record<string, DynamicJsonFormRef | null>>({}); const formRefs = useRef<Record<string, DynamicJsonFormRef | null>>({});
const { toast } = useToast(); const { toast } = useToast();
const { copied, setCopied } = useCopy(); const { copied, setCopied } = useCopy();
// Function to check if any form has validation errors
const checkValidationErrors = () => {
const errors = Object.values(formRefs.current).some(
(ref) => ref && !ref.validateJson().isValid,
);
setHasValidationErrors(errors);
return errors;
};
useEffect(() => { useEffect(() => {
const params = Object.entries( const params = Object.entries(
selectedTool?.inputSchema.properties ?? [], selectedTool?.inputSchema.properties ?? [],
@ -82,6 +92,12 @@ const ToolsTab = ({
), ),
]); ]);
setParams(Object.fromEntries(params)); setParams(Object.fromEntries(params));
// Reset validation errors when switching tools
setHasValidationErrors(false);
// Clear form refs for the previous tool
formRefs.current = {};
}, [selectedTool]); }, [selectedTool]);
return ( return (
@ -199,6 +215,8 @@ const ToolsTab = ({
...params, ...params,
[key]: newValue, [key]: newValue,
}); });
// Check validation after a short delay to allow form to update
setTimeout(checkValidationErrors, 100);
}} }}
/> />
</div> </div>
@ -235,6 +253,8 @@ const ToolsTab = ({
...params, ...params,
[key]: newValue, [key]: newValue,
}); });
// Check validation after a short delay to allow form to update
setTimeout(checkValidationErrors, 100);
}} }}
/> />
</div> </div>
@ -317,10 +337,7 @@ const ToolsTab = ({
<Button <Button
onClick={async () => { onClick={async () => {
// Validate JSON inputs before calling tool // Validate JSON inputs before calling tool
const hasValidationErrors = Object.values( if (checkValidationErrors()) return;
formRefs.current,
).some((ref) => ref && !ref.validateJson().isValid);
if (hasValidationErrors) return;
try { try {
setIsToolRunning(true); setIsToolRunning(true);
@ -329,7 +346,7 @@ const ToolsTab = ({
setIsToolRunning(false); setIsToolRunning(false);
} }
}} }}
disabled={isToolRunning} disabled={isToolRunning || hasValidationErrors}
> >
{isToolRunning ? ( {isToolRunning ? (
<> <>