import { CameraSelect } from "@/components/CameraSelect"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { ScrollArea } from "@/components/ui/scroll-area"; import { readStoredCameraIndex, useCameras, writeStoredCameraIndex, } from "@/hooks/useCameras"; import { apiPath } from "@/lib/api"; import { Play, Search, Terminal, Wrench } from "lucide-react"; import { useEffect, useState } from "react"; interface ToolParameter { name: string; type: string; description?: string; required: boolean; } interface ToolInfo { name: string; description: string; parameters: ToolParameter[]; } export function Tools() { const [tools, setTools] = useState([]); const [selectedTool, setSelectedTool] = useState(null); const [args, setArgs] = useState>({}); const [result, setResult] = useState(null); const [loading, setLoading] = useState(false); const { cameras, loading: camerasLoading, error: camerasError, refresh: refreshCameras, } = useCameras(); const hasCameraParam = Boolean( selectedTool?.parameters.some((p) => p.name === "camera_index"), ); useEffect(() => { fetchTools(); }, []); useEffect(() => { if (!hasCameraParam) return; if (cameras.length === 0) return; const stored = readStoredCameraIndex(); const pick = stored !== null && cameras.some((c) => c.index === stored) ? stored : cameras[0].index; setArgs((prev) => { if ( typeof prev.camera_index === "number" && cameras.some((c) => c.index === prev.camera_index) ) { return prev; } return { ...prev, camera_index: pick }; }); }, [selectedTool, cameras, hasCameraParam]); const fetchTools = async () => { try { const response = await fetch(apiPath("/api/v1/tools/")); const data = await response.json(); setTools(data); } catch (error) { console.error("Error fetching tools:", error); } }; const handleCallTool = async () => { if (!selectedTool) return; setLoading(true); try { const response = await fetch(apiPath("/api/v1/tools/call"), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: selectedTool.name, arguments: args, }), }); const data = await response.json(); setResult(data); } catch (error) { setResult({ status: "error", message: String(error) }); } finally { setLoading(false); } }; return (

MCP Tools Hub

Uses the live REST bridge ( /api/v1/tools) when the backend is running — not mock data.

windows-computer-use-mcp
{/* Tool List */} Available Tools Select an automation tool to execute
{tools.map((tool) => ( ))}
{/* Tool Execution */} {selectedTool ? `Run: ${selectedTool.name}` : "Select a Tool"} {selectedTool?.description || "Pick a tool from the left to begin execution"} {selectedTool && ( <> {hasCameraParam && cameras.length > 0 && ( void refreshCameras()} value={ typeof args.camera_index === "number" ? args.camera_index : cameras[0].index } onChange={(index) => { writeStoredCameraIndex(index); setArgs((prev) => ({ ...prev, camera_index: index })); }} id="tools-camera-index" label="camera_index" /> )} {hasCameraParam && !camerasLoading && cameras.length === 0 && (
setArgs((prev) => ({ ...prev, camera_index: Number(e.target.value) || 0, })) } />

No cameras auto-detected — enter OpenCV index manually or refresh after plugging in a USB webcam.

)}
{selectedTool.parameters .filter((param) => param.name !== "camera_index") .map((param) => (
{param.type}
{ let val: unknown = e.target.value; if ( param.type === "integer" || param.type === "number" ) { val = Number(val); } else if (param.type === "array") { try { val = JSON.parse(e.target.value) as unknown; } catch { val = e.target.value; } } setArgs((prev) => ({ ...prev, [param.name]: val })); }} /> {param.description && (

{param.description}

)}
))}
)} {/* Results Console */} {result != null && (
{JSON.stringify(result, null, 2)}
)}
); }