Add JSON validation to tool execution

Prevents execution of tools with invalid JSON parameters by validating JSON
syntax before tool execution and showing error UI for invalid input.

- Add validateJson method to DynamicJsonForm via forwardRef
- Add validation check in ToolsTab before tool execution
- Reuse existing JsonEditor error display for consistent UX
This commit is contained in:
Nandha Reddy 2025-06-19 02:32:40 +10:00
commit d65ed01922

View file

@ -1,11 +1,11 @@
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { TabsContent } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea";
import DynamicJsonForm from "./DynamicJsonForm";
import DynamicJsonForm, { DynamicJsonFormRef } from "./DynamicJsonForm";
import type { JsonValue, JsonSchemaType } from "@/utils/jsonUtils";
import { generateDefaultValue } from "@/utils/schemaUtils";
import {
@ -13,8 +13,8 @@ import {
ListToolsResult,
Tool,
} from "@modelcontextprotocol/sdk/types.js";
import { Loader2, Send, ChevronDown, ChevronUp } from "lucide-react";
import { useEffect, useState } from "react";
import { Loader2, Send, ChevronDown, ChevronUp, AlertCircle } from "lucide-react";
import { useEffect, useState, useRef } from "react";
import ListPane from "./ListPane";
import JsonView from "./JsonView";
import ToolResults from "./ToolResults";
@ -28,6 +28,7 @@ const ToolsTab = ({
setSelectedTool,
toolResult,
nextCursor,
error,
}: {
tools: Tool[];
listTools: () => void;
@ -42,6 +43,7 @@ const ToolsTab = ({
const [params, setParams] = useState<Record<string, unknown>>({});
const [isToolRunning, setIsToolRunning] = useState(false);
const [isOutputSchemaExpanded, setIsOutputSchemaExpanded] = useState(false);
const formRefs = useRef<Record<string, DynamicJsonFormRef | null>>({});
useEffect(() => {
const params = Object.entries(
@ -84,7 +86,13 @@ const ToolsTab = ({
</h3>
</div>
<div className="p-4">
{selectedTool ? (
{error ? (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertTitle>Error</AlertTitle>
<AlertDescription>{error}</AlertDescription>
</Alert>
) : selectedTool ? (
<div className="space-y-4">
<p className="text-sm text-gray-600 dark:text-gray-400">
{selectedTool.description}
@ -137,6 +145,7 @@ const ToolsTab = ({
) : prop.type === "object" || prop.type === "array" ? (
<div className="mt-1">
<DynamicJsonForm
ref={(ref) => (formRefs.current[key] = ref)}
schema={{
type: prop.type,
properties: prop.properties,
@ -174,6 +183,7 @@ const ToolsTab = ({
) : (
<div className="mt-1">
<DynamicJsonForm
ref={(ref) => (formRefs.current[key] = ref)}
schema={{
type: prop.type,
properties: prop.properties,
@ -232,6 +242,12 @@ const ToolsTab = ({
)}
<Button
onClick={async () => {
// Validate JSON inputs before calling tool
const hasValidationErrors = Object.values(formRefs.current).some(
(ref) => ref && !ref.validateJson().isValid
);
if (hasValidationErrors) return;
try {
setIsToolRunning(true);
await callTool(selectedTool.name, params);