Add real-time JSON validation and prevent tool execution on invalid input
This commit is contained in:
parent
119922b8b5
commit
8d149b2673
2 changed files with 39 additions and 8 deletions
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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 ? (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue