import React, { useState } from 'react'; import { FiX, FiTool, FiInfo, FiCheck, FiCircle, FiChevronRight, FiChevronDown, FiSearch } from 'react-icons/fi'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../ui/dialog'; import { Button } from '../ui/Button'; import Typography from '../ui/Typography'; import { Card } from '../ui/Card'; import { MCPServerTool } from '../../types/mcp'; interface MCPToolsModalProps { isOpen: boolean; onClose: () => void; serverName: string; tools: MCPServerTool[]; } interface ParameterInfo { name: string; type: string; description?: string; required: boolean; default?: any; enum?: string[]; } const parseJsonSchema = (schema: any): ParameterInfo[] => { if (!schema || !schema.properties) return []; const required = schema.required || []; const parameters: ParameterInfo[] = []; Object.entries(schema.properties).forEach(([name, prop]: [string, any]) => { parameters.push({ name, type: prop.type || 'unknown', description: prop.description, required: required.includes(name), default: prop.default, enum: prop.enum }); }); return parameters; }; const getTypeColor = (type: string) => { switch (type.toLowerCase()) { case 'string': return 'text-blue-600 bg-blue-50 dark:text-blue-400 dark:bg-blue-900/20'; case 'number': return 'text-green-600 bg-green-50 dark:text-green-400 dark:bg-green-900/20'; case 'boolean': return 'text-purple-600 bg-purple-50 dark:text-purple-400 dark:bg-purple-900/20'; case 'array': return 'text-orange-600 bg-orange-50 dark:text-orange-400 dark:bg-orange-900/20'; case 'object': return 'text-indigo-600 bg-indigo-50 dark:text-indigo-400 dark:bg-indigo-900/20'; default: return 'text-gray-600 bg-gray-50 dark:text-gray-400 dark:bg-gray-900/20'; } }; export const MCPToolsModal: React.FC = ({ isOpen, onClose, serverName, tools }) => { const [searchTerm, setSearchTerm] = useState(''); const [expandedTools, setExpandedTools] = useState>(new Set()); const filteredTools = tools.filter(tool => tool.name.toLowerCase().includes(searchTerm.toLowerCase()) || tool.description.toLowerCase().includes(searchTerm.toLowerCase()) ); const toggleToolExpansion = (index: number) => { const newExpanded = new Set(expandedTools); if (newExpanded.has(index)) { newExpanded.delete(index); } else { newExpanded.add(index); } setExpandedTools(newExpanded); }; return (
{serverName} Tools {filteredTools.length} of {tools.length} tools
setSearchTerm(e.target.value)} className="w-full pl-10 pr-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 focus:ring-2 focus:ring-primary-500 focus:border-transparent" />
{filteredTools.map((tool, index) => { const parameters = parseJsonSchema(tool.inputSchema); const requiredParams = parameters.filter(p => p.required); const optionalParams = parameters.filter(p => !p.required); const isExpanded = expandedTools.has(index); return (
toggleToolExpansion(index)} >
{isExpanded ? ( ) : ( )} {tool.name}
{parameters.length} param{parameters.length !== 1 ? 's' : ''} {requiredParams.length > 0 && ( {requiredParams.length} required )}
{tool.description}
{isExpanded && (
{parameters.length > 0 ? (
{requiredParams.length > 0 && (
Required Parameters
{requiredParams.map((param, paramIndex) => (
{param.name} {param.type}
{param.description && ( {param.description} )} {param.enum && ( Options: {param.enum.join(', ')} )}
))}
)} {optionalParams.length > 0 && (
Optional Parameters
{optionalParams.map((param, paramIndex) => (
{param.name} {param.type} {param.default !== undefined && ( default: {String(param.default)} )}
{param.description && ( {param.description} )} {param.enum && ( Options: {param.enum.join(', ')} )}
))}
)}
) : (
No parameters required for this tool
)}
)}
); })} {filteredTools.length === 0 && (
No tools found matching "{searchTerm}"
)}
Click on any tool to expand and view its parameters
); };