import React, { useState, useRef, useEffect } from 'react' import { X, Info, Settings, Plus, Trash2, Power, PowerOff } from 'lucide-react' import { useMcp, type Tool } from 'use-mcp/react' interface McpServer { id: string url: string enabled: boolean name?: string } // MCP Connection wrapper for a single server function McpConnection({ server, onConnectionUpdate, }: { server: McpServer onConnectionUpdate: (serverId: string, data: any) => void }) { // Use the MCP hook with the server URL const connection = useMcp({ url: server.url, debug: true, autoRetry: false, popupFeatures: 'width=500,height=600,resizable=yes,scrollbars=yes', }) // Update parent component with connection data useEffect(() => { onConnectionUpdate(server.id, connection) }, [ server.id, connection.state, connection.tools, connection.error, connection.log.length, connection.authUrl, ]) // Return null as this is just a hook wrapper return null } interface McpServerModalProps { isOpen: boolean onClose: () => void onToolsUpdate?: (tools: Tool[]) => void } const McpServerModal: React.FC = ({ isOpen, onClose, onToolsUpdate, }) => { const [servers, setServers] = useState(() => { const stored = localStorage.getItem('mcpServers') return stored ? JSON.parse(stored) : [] }) const [newServerUrl, setNewServerUrl] = useState('') const [showSettings, setShowSettings] = useState(false) const [connectionData, setConnectionData] = useState>({}) const [serverToolCounts, setServerToolCounts] = useState>(() => { const stored = localStorage.getItem('mcpServerToolCounts') return stored ? JSON.parse(stored) : {} }) const logRef = useRef(null) // Save servers to localStorage whenever they change useEffect(() => { localStorage.setItem('mcpServers', JSON.stringify(servers)) }, [servers]) // Save tool counts to localStorage whenever they change useEffect(() => { localStorage.setItem('mcpServerToolCounts', JSON.stringify(serverToolCounts)) }, [serverToolCounts]) useEffect(() => { if (isOpen) { document.body.style.overflow = 'hidden' } else { document.body.style.overflow = 'unset' } return () => { document.body.style.overflow = 'unset' } }, [isOpen]) // Aggregate all tools from enabled servers and notify parent useEffect(() => { const allTools: Tool[] = [] servers.forEach(server => { if (server.enabled && connectionData[server.id]?.tools) { const serverTools = connectionData[server.id].tools.map((t: Tool) => ({ ...t, callTool: (args: Record) => connectionData[server.id].callTool(t.name, args), })) allTools.push(...serverTools) } }) if (onToolsUpdate) { onToolsUpdate(allTools) } }, [servers, connectionData, onToolsUpdate]) // Handle adding a new server const handleAddServer = () => { if (!newServerUrl.trim()) return const newServer: McpServer = { id: Date.now().toString(), url: newServerUrl.trim(), enabled: true, name: new URL(newServerUrl.trim()).hostname } setServers(prev => [...prev, newServer]) setNewServerUrl('') } // Handle removing a server const handleRemoveServer = (serverId: string) => { // Disconnect if connected const connection = connectionData[serverId] if (connection?.disconnect) { connection.disconnect() } setServers(prev => prev.filter(s => s.id !== serverId)) setConnectionData(prev => { const newData = { ...prev } delete newData[serverId] return newData }) setServerToolCounts(prev => { const newCounts = { ...prev } delete newCounts[serverId] return newCounts }) } // Handle toggling server enabled state const handleToggleServer = (serverId: string) => { setServers(prev => prev.map(server => server.id === serverId ? { ...server, enabled: !server.enabled } : server )) // If disabling, disconnect const server = servers.find(s => s.id === serverId) if (server?.enabled && connectionData[serverId]?.disconnect) { connectionData[serverId].disconnect() } } // Handle connection update for a specific server const handleConnectionUpdate = (serverId: string, data: any) => { setConnectionData(prev => ({ ...prev, [serverId]: data })) // Store tool count for this server (even if it gets disabled later) if (data.tools && Array.isArray(data.tools)) { setServerToolCounts(prev => ({ ...prev, [serverId]: data.tools.length })) } } // Handle authentication for a specific server const handleManualAuth = async (serverId: string) => { try { const connection = connectionData[serverId] if (connection?.authenticate) { await connection.authenticate() } } catch (err) { console.error('Authentication error:', err) } } // Generate status badge based on connection state const getStatusBadge = (state: string) => { const baseClasses = 'px-2 py-1 rounded-full text-xs font-medium' switch (state) { case 'discovering': return ( Discovering ) case 'authenticating': return ( Authenticating ) case 'connecting': return ( Connecting ) case 'loading': return ( Loading ) case 'ready': return ( Connected ) case 'failed': return ( Failed ) case 'not-connected': default: return ( Not Connected ) } } return ( <>
e.stopPropagation()} >

MCP Servers

Connect to Model Context Protocol (MCP) servers to access additional AI capabilities.
{/* Server List */}
{servers.map((server) => { const connection = connectionData[server.id] || { state: 'not-connected', tools: [], error: undefined, authUrl: undefined } const { state, tools, error, authUrl } = connection return (
{server.name || 'MCP Server'}
{server.url}
{getStatusBadge(server.enabled ? state : 'disabled')}
{server.enabled && ( <> {error && state === 'failed' && (
{error}
)} {authUrl && (

Authentication required. Please click the link below:

handleManualAuth(server.id)} > Authenticate in new window
)} {state === 'ready' && tools.length > 0 && (

Available Tools ({tools.length})

{tools.map((tool: Tool, index: number) => (
{tool.name} {tool.description && ( - {tool.description} )}
))}
)} )}
) })} {servers.length === 0 && (

No MCP servers configured yet.

Add your first server below.

)}
{/* Add New Server */}

Add New Server

setNewServerUrl(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault() handleAddServer() } }} />
{/* Debug Info */} {showSettings && (

Debug Information

Total Servers: {servers.length}
Enabled Servers: {servers.filter(s => s.enabled).length}
Connected Servers: {Object.values(connectionData).filter((c: any) => c.state === 'ready').length}
Total Tools: {Object.values(connectionData).reduce((sum: number, c: any) => sum + (c.tools?.length || 0), 0)}
)}
{/* Render MCP connections for all enabled servers */} {servers.filter(s => s.enabled).map(server => ( ))} ) } export default McpServerModal