import { createFileRoute } from '@tanstack/react-router' import { route } from '@/constants/routes' import HeaderPage from '@/containers/HeaderPage' import SettingsMenu from '@/containers/SettingsMenu' import { t } from 'i18next' import { Card, CardItem } from '@/containers/Card' import { IconPencil, IconPlus, IconTrash, IconCodeCircle, } from '@tabler/icons-react' import { useMCPServers, MCPServerConfig } from '@/hooks/useMCPServers' import { useEffect, useState } from 'react' import AddEditMCPServer from '@/containers/dialogs/AddEditMCPServer' import DeleteMCPServerConfirm from '@/containers/dialogs/DeleteMCPServerConfirm' import EditJsonMCPserver from '@/containers/dialogs/EditJsonMCPserver' import { Switch } from '@/components/ui/switch' import { twMerge } from 'tailwind-merge' import { getConnectedServers } from '@/services/mcp' import { useToolApproval } from '@/hooks/useToolApproval' import { toast } from 'sonner' import { invoke } from '@tauri-apps/api/core' // eslint-disable-next-line @typescript-eslint/no-explicit-any export const Route = createFileRoute(route.settings.mcp_servers as any)({ component: MCPServers, }) function MCPServers() { const { mcpServers, addServer, editServer, deleteServer, syncServers, syncServersAndRestart, getServerConfig, } = useMCPServers() const { allowAllMCPPermissions, setAllowAllMCPPermissions } = useToolApproval() const [open, setOpen] = useState(false) const [editingKey, setEditingKey] = useState(null) const [currentConfig, setCurrentConfig] = useState< MCPServerConfig | undefined >(undefined) // Delete confirmation dialog state const [deleteDialogOpen, setDeleteDialogOpen] = useState(false) const [serverToDelete, setServerToDelete] = useState(null) // JSON editor dialog state const [jsonEditorOpen, setJsonEditorOpen] = useState(false) const [jsonServerName, setJsonServerName] = useState(null) const [jsonEditorData, setJsonEditorData] = useState< MCPServerConfig | Record | undefined >(undefined) const [connectedServers, setConnectedServers] = useState([]) const [loadingServers, setLoadingServers] = useState<{ [key: string]: boolean }>({}) const handleOpenDialog = (serverKey?: string) => { if (serverKey) { // Edit mode setCurrentConfig(mcpServers[serverKey]) setEditingKey(serverKey) } else { // Add mode setCurrentConfig(undefined) setEditingKey(null) } setOpen(true) } const handleSaveServer = async (name: string, config: MCPServerConfig) => { try { await toggleServer(name, false) } catch (error) { console.error('Error deactivating server:', error) } if (editingKey) { // If server name changed, delete old one and add new one if (editingKey !== name) { deleteServer(editingKey) addServer(name, config) } else { editServer(editingKey, config) } } else { // Add new server addServer(name, config) } syncServers() await toggleServer(name, true) } const handleEdit = (serverKey: string) => { handleOpenDialog(serverKey) } const handleDeleteClick = (serverKey: string) => { setServerToDelete(serverKey) setDeleteDialogOpen(true) } const handleConfirmDelete = () => { if (serverToDelete) { deleteServer(serverToDelete) setServerToDelete(null) syncServersAndRestart() } } const handleOpenJsonEditor = async (serverKey?: string) => { if (serverKey) { // Edit single server JSON setJsonServerName(serverKey) setJsonEditorData(mcpServers[serverKey]) } else { // Edit all servers JSON setJsonServerName(null) setJsonEditorData(mcpServers) } setJsonEditorOpen(true) } const handleSaveJson = async ( data: MCPServerConfig | Record ) => { if (jsonServerName) { try { await toggleServer(jsonServerName, false) } catch (error) { console.error('Error deactivating server:', error) } // Save single server editServer(jsonServerName, data as MCPServerConfig) syncServers() toggleServer(jsonServerName, true) } else { // Save all servers // Clear existing servers first Object.keys(mcpServers).forEach((key) => { deleteServer(key) }) // Add all servers from the JSON Object.entries(data as Record).forEach( ([key, config]) => { addServer(key, config) } ) } } const toggleServer = (serverKey: string, active: boolean) => { if (serverKey) { setLoadingServers((prev) => ({ ...prev, [serverKey]: true })) const config = getServerConfig(serverKey) if (active && config) { invoke('activate_mcp_server', { name: serverKey, config: { ...(config ?? (mcpServers[serverKey] as MCPServerConfig)), active, }, }) .then(() => { // Save single server editServer(serverKey, { ...(config ?? (mcpServers[serverKey] as MCPServerConfig)), active, }) syncServers() toast.success( `Server ${serverKey} is now ${active ? 'active' : 'inactive'}.` ) getConnectedServers().then(setConnectedServers) }) .catch((error) => { editServer(serverKey, { ...(config ?? (mcpServers[serverKey] as MCPServerConfig)), active: false, }) toast.error(error, { description: 'Please check the parameters according to the tutorial.', }) }) .finally(() => { setLoadingServers((prev) => ({ ...prev, [serverKey]: false })) }) } else { editServer(serverKey, { ...(config ?? (mcpServers[serverKey] as MCPServerConfig)), active, }) syncServers() invoke('deactivate_mcp_server', { name: serverKey }).finally(() => { getConnectedServers().then(setConnectedServers) setLoadingServers((prev) => ({ ...prev, [serverKey]: false })) }) } } } useEffect(() => { getConnectedServers().then(setConnectedServers) const intervalId = setInterval(() => { getConnectedServers().then(setConnectedServers) }, 3000) return () => clearInterval(intervalId) }, [setConnectedServers]) return (

{t('common.settings')}

MCP Servers

handleOpenJsonEditor()} title="Edit All Servers JSON" >
handleOpenDialog()} title="Add Server" >

Find more MCP servers at{' '} mcp.so

} >
} /> {Object.keys(mcpServers).length === 0 ? (
No MCP servers found
) : ( Object.entries(mcpServers).map(([key, config], index) => (

{key}

} description={
Command: {config.command}
Args: {config?.args?.join(', ')}
{config.env && Object.keys(config.env).length > 0 && (
Env:{' '} {Object.entries(config.env) .map(([key, value]) => `${key}=${value}`) .join(', ')}
)}
} actions={
handleOpenJsonEditor(key)} title="Edit JSON" >
handleEdit(key)} title="Edit Server" >
handleDeleteClick(key)} title="Delete Server" >
toggleServer(key, checked) } />
} />
)) )}
{/* Use the AddEditMCPServer component */} {/* Delete confirmation dialog */} {/* JSON editor dialog */} } onSave={handleSaveJson} /> ) }