import React, { useState, useMemo, useCallback } from 'react'; import { ChevronLeft, Trash2 } from 'lucide-react'; import { useQueryClient } from '@tanstack/react-query'; import { Button, useToastContext } from '@librechat/client'; import { Constants, QueryKeys } from 'librechat-data-provider'; import { useUpdateUserPluginsMutation } from 'librechat-data-provider/react-query'; import type { TUpdateUserPlugins } from 'librechat-data-provider'; import ServerInitializationSection from '~/components/MCP/ServerInitializationSection'; import CustomUserVarsSection from '~/components/MCP/CustomUserVarsSection'; import { MCPPanelProvider, useMCPPanelContext } from '~/Providers'; import { useLocalize, useMCPConnectionStatus } from '~/hooks'; import { useGetStartupConfig } from '~/data-provider'; import MCPPanelSkeleton from './MCPPanelSkeleton'; function MCPPanelContent() { const localize = useLocalize(); const queryClient = useQueryClient(); const { showToast } = useToastContext(); const { conversationId } = useMCPPanelContext(); const { data: startupConfig, isLoading: startupConfigLoading } = useGetStartupConfig(); const { connectionStatus } = useMCPConnectionStatus({ enabled: !!startupConfig?.mcpServers && Object.keys(startupConfig.mcpServers).length > 0, }); const [selectedServerNameForEditing, setSelectedServerNameForEditing] = useState( null, ); const updateUserPluginsMutation = useUpdateUserPluginsMutation({ onSuccess: async () => { showToast({ message: localize('com_nav_mcp_vars_updated'), status: 'success' }); await Promise.all([ queryClient.invalidateQueries([QueryKeys.mcpTools]), queryClient.invalidateQueries([QueryKeys.mcpAuthValues]), queryClient.invalidateQueries([QueryKeys.mcpConnectionStatus]), ]); }, onError: (error: unknown) => { console.error('Error updating MCP auth:', error); showToast({ message: localize('com_nav_mcp_vars_update_error'), status: 'error', }); }, }); const mcpServerDefinitions = useMemo(() => { if (!startupConfig?.mcpServers) { return []; } return Object.entries(startupConfig.mcpServers).map(([serverName, config]) => ({ serverName, iconPath: null, config: { ...config, customUserVars: config.customUserVars ?? {}, }, })); }, [startupConfig?.mcpServers]); const handleServerClickToEdit = (serverName: string) => { setSelectedServerNameForEditing(serverName); }; const handleGoBackToList = () => { setSelectedServerNameForEditing(null); }; const handleConfigSave = useCallback( (targetName: string, authData: Record) => { console.log( `[MCP Panel] Saving config for ${targetName}, pluginKey: ${`${Constants.mcp_prefix}${targetName}`}`, ); const payload: TUpdateUserPlugins = { pluginKey: `${Constants.mcp_prefix}${targetName}`, action: 'install', auth: authData, }; updateUserPluginsMutation.mutate(payload); }, [updateUserPluginsMutation], ); const handleConfigRevoke = useCallback( (targetName: string) => { const payload: TUpdateUserPlugins = { pluginKey: `${Constants.mcp_prefix}${targetName}`, action: 'uninstall', auth: {}, }; updateUserPluginsMutation.mutate(payload); }, [updateUserPluginsMutation], ); if (startupConfigLoading) { return ; } if (mcpServerDefinitions.length === 0) { return (
{localize('com_sidepanel_mcp_no_servers_with_vars')}
); } if (selectedServerNameForEditing) { // Editing View const serverBeingEdited = mcpServerDefinitions.find( (s) => s.serverName === selectedServerNameForEditing, ); if (!serverBeingEdited) { // Fallback to list view if server not found setSelectedServerNameForEditing(null); return (
{localize('com_ui_error')}: {localize('com_ui_mcp_server_not_found')}
); } const serverStatus = connectionStatus?.[selectedServerNameForEditing]; const isConnected = serverStatus?.connectionState === 'connected'; return (
{ if (selectedServerNameForEditing) { handleConfigSave(selectedServerNameForEditing, authData); } }} onRevoke={() => { if (selectedServerNameForEditing) { handleConfigRevoke(selectedServerNameForEditing); } }} isSubmitting={updateUserPluginsMutation.isLoading} />
0 } /> {serverStatus?.requiresOAuth && isConnected && ( )}
); } else { // Server List View return (
{mcpServerDefinitions.map((server) => { const serverStatus = connectionStatus?.[server.serverName]; const isConnected = serverStatus?.connectionState === 'connected'; return (
); })}
); } } export default function MCPPanel() { return ( ); }