diff --git a/client/src/components/DynamicJsonForm.tsx b/client/src/components/DynamicJsonForm.tsx index 6a5993c3..fe2cfd2d 100644 --- a/client/src/components/DynamicJsonForm.tsx +++ b/client/src/components/DynamicJsonForm.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback, useRef } from "react"; +import { useState, useEffect, useCallback, useRef, forwardRef, useImperativeHandle } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import JsonEditor from "./JsonEditor"; @@ -13,6 +13,10 @@ interface DynamicJsonFormProps { maxDepth?: number; } +export interface DynamicJsonFormRef { + validateJson: () => { isValid: boolean; error: string | null }; +} + const isSimpleObject = (schema: JsonSchemaType): boolean => { const supportedTypes = ["string", "number", "integer", "boolean", "null"]; if (supportedTypes.includes(schema.type)) return true; @@ -22,12 +26,12 @@ const isSimpleObject = (schema: JsonSchemaType): boolean => { ); }; -const DynamicJsonForm = ({ +const DynamicJsonForm = forwardRef(({ schema, value, onChange, maxDepth = 3, -}: DynamicJsonFormProps) => { +}, ref) => { const isOnlyJSON = !isSimpleObject(schema); const [isJsonMode, setIsJsonMode] = useState(isOnlyJSON); const [jsonError, setJsonError] = useState(); @@ -108,6 +112,25 @@ const DynamicJsonForm = ({ } }; + const validateJson = () => { + if (!isJsonMode) return { isValid: true, error: null }; + try { + const jsonStr = rawJsonValue.trim(); + if (!jsonStr) return { isValid: true, error: null }; + JSON.parse(jsonStr); + setJsonError(undefined); + return { isValid: true, error: null }; + } catch (err) { + const errorMessage = err instanceof Error ? err.message : "Invalid JSON"; + setJsonError(errorMessage); + return { isValid: false, error: errorMessage }; + } + }; + + useImperativeHandle(ref, () => ({ + validateJson, + })); + const renderFormFields = ( propSchema: JsonSchemaType, currentValue: JsonValue, @@ -303,6 +326,6 @@ const DynamicJsonForm = ({ )} ); -}; +}); export default DynamicJsonForm; diff --git a/client/src/components/ToolsTab.tsx b/client/src/components/ToolsTab.tsx index 8a7f6578..1ce034fb 100644 --- a/client/src/components/ToolsTab.tsx +++ b/client/src/components/ToolsTab.tsx @@ -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>({}); const [isToolRunning, setIsToolRunning] = useState(false); const [isOutputSchemaExpanded, setIsOutputSchemaExpanded] = useState(false); + const formRefs = useRef>({}); useEffect(() => { const params = Object.entries( @@ -84,7 +86,13 @@ const ToolsTab = ({
- {selectedTool ? ( + {error ? ( + + + Error + {error} + + ) : selectedTool ? (

{selectedTool.description} @@ -137,6 +145,7 @@ const ToolsTab = ({ ) : prop.type === "object" || prop.type === "array" ? (

(formRefs.current[key] = ref)} schema={{ type: prop.type, properties: prop.properties, @@ -174,6 +183,7 @@ const ToolsTab = ({ ) : (
(formRefs.current[key] = ref)} schema={{ type: prop.type, properties: prop.properties, @@ -232,6 +242,12 @@ const ToolsTab = ({ )}