import React from "react"; import { ColumnDef } from "@tanstack/react-table"; import { MCPTool, InputSchema } from "./types"; import { Button } from "@tremor/react" export const columns: ColumnDef[] = [ { accessorKey: "mcp_info.server_name", header: "Provider", cell: ({ row }) => { const serverName = row.original.mcp_info.server_name; const logoUrl = row.original.mcp_info.logo_url; return (
{logoUrl && ( {`${serverName} )} {serverName}
); }, }, { accessorKey: "name", header: "Tool Name", cell: ({ row }) => { const name = row.getValue("name") as string; return (
{name}
); }, }, { accessorKey: "description", header: "Description", cell: ({ row }) => { const description = row.getValue("description") as string; return (
{description}
); }, }, { id: "actions", header: "Actions", cell: ({ row }) => { const tool = row.original; return (
); }, }, ]; // Tool Panel component to display when a tool is selected export function ToolTestPanel({ tool, onSubmit, isLoading, result, error, onClose }: { tool: MCPTool; onSubmit: (args: Record) => void; isLoading: boolean; result: any | null; error: Error | null; onClose: () => void; }) { const [formState, setFormState] = React.useState>({}); // Create a placeholder schema if we only have the "tool_input_schema" string const schema: InputSchema = React.useMemo(() => { if (typeof tool.inputSchema === 'string') { // Default schema with a single text field return { type: "object", properties: { input: { type: "string", description: "Input for this tool" } }, required: ["input"] }; } return tool.inputSchema as InputSchema; }, [tool.inputSchema]); const handleInputChange = (key: string, value: any) => { setFormState(prev => ({ ...prev, [key]: value })); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); onSubmit(formState); }; return (

Test Tool: {tool.name}

{tool.description}

Provider: {tool.mcp_info.server_name}

{/* Form Section */}

Input Parameters

{typeof tool.inputSchema === 'string' ? (

This tool uses a dynamic input schema.

handleInputChange("input", e.target.value)} required className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
) : ( Object.entries(schema.properties).map(([key, prop]) => (
{prop.description && (

{prop.description}

)} {/* Render appropriate input based on type */} {prop.type === "string" && ( handleInputChange(key, e.target.value)} required={schema.required?.includes(key)} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" /> )} {prop.type === "number" && ( handleInputChange(key, parseFloat(e.target.value))} required={schema.required?.includes(key)} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" /> )} {prop.type === "boolean" && (
handleInputChange(key, e.target.checked)} className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" /> Enable
)}
)) )}
{/* Result Section */}

Result

{isLoading && (
)} {error && (

Error

{error.message}
)} {result && !isLoading && !error && (
{result.map((content: any, idx: number) => (
{content.type === "text" && (

{content.text}

)} {content.type === "image" && content.url && (
Tool result
)} {content.type === "embedded_resource" && (

Embedded Resource

Type: {content.resource_type}

{content.url && ( View Resource )}
)}
))}
Raw JSON Response
                    {JSON.stringify(result, null, 2)}
                  
)} {!result && !isLoading && !error && (

The result will appear here after you call the tool.

)}
); }