diff --git a/client/src/components/MCP/CustomUserVarsSection.tsx b/client/src/components/MCP/CustomUserVarsSection.tsx index c178fae24..051634249 100644 --- a/client/src/components/MCP/CustomUserVarsSection.tsx +++ b/client/src/components/MCP/CustomUserVarsSection.tsx @@ -1,6 +1,6 @@ import React, { useMemo } from 'react'; import { useForm, Controller } from 'react-hook-form'; -import { Input, Label, Button } from '@librechat/client'; +import { Input, Label, Button, TooltipAnchor, CircleHelpIcon } from '@librechat/client'; import { useMCPAuthValuesQuery } from '~/data-provider/Tools/queries'; import { useLocalize } from '~/hooks'; @@ -31,16 +31,24 @@ function AuthField({ name, config, hasValue, control, errors }: AuthFieldProps) return (
- + + + +
+ } + /> {hasValue ? ( -
+
{localize('com_ui_set')}
) : ( -
+
{localize('com_ui_unset')}
@@ -60,16 +68,10 @@ function AuthField({ name, config, hasValue, control, errors }: AuthFieldProps) ? localize('com_ui_mcp_update_var', { 0: config.title }) : localize('com_ui_mcp_enter_var', { 0: config.title }) } - className="w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white sm:text-sm" + className="w-full shadow-sm sm:text-sm" /> )} /> - {config.description && ( -

- )} {errors[name] &&

{errors[name]?.message}

}
); @@ -114,15 +116,11 @@ export default function CustomUserVarsSection({ }; if (!fields || Object.keys(fields).length === 0) { - return ( -
- {localize('com_sidepanel_mcp_no_custom_vars', { '0': serverName })} -
- ); + return null; } return ( -
+
{Object.entries(fields).map(([key, config]) => { const hasValue = authValuesData?.authValueFlags?.[key] || false; @@ -140,21 +138,11 @@ export default function CustomUserVarsSection({ })}
-
- -
diff --git a/client/src/components/MCP/MCPConfigDialog.tsx b/client/src/components/MCP/MCPConfigDialog.tsx index 5bcd590ec..7c4c86fce 100644 --- a/client/src/components/MCP/MCPConfigDialog.tsx +++ b/client/src/components/MCP/MCPConfigDialog.tsx @@ -1,11 +1,11 @@ import React from 'react'; -import { Loader2, KeyRound, PlugZap, AlertTriangle } from 'lucide-react'; +import { KeyRound, PlugZap, AlertTriangle } from 'lucide-react'; import { + Spinner, OGDialog, OGDialogTitle, OGDialogHeader, OGDialogContent, - OGDialogDescription, } from '@librechat/client'; import type { MCPServerStatus } from 'librechat-data-provider'; import ServerInitializationSection from './ServerInitializationSection'; @@ -45,9 +45,6 @@ export default function MCPConfigDialog({ const dialogTitle = hasFields ? localize('com_ui_configure_mcp_variables_for', { 0: serverName }) : `${serverName} MCP Server`; - const dialogDescription = hasFields - ? localize('com_ui_mcp_dialog_desc') - : `Manage connection and settings for the ${serverName} MCP server.`; // Helper function to render status badge based on connection state const renderStatusBadge = () => { @@ -60,7 +57,7 @@ export default function MCPConfigDialog({ if (connectionState === 'connecting') { return (
- + {localize('com_ui_connecting')}
); @@ -107,26 +104,24 @@ export default function MCPConfigDialog({ return ( - +
- {dialogTitle} + + {dialogTitle.charAt(0).toUpperCase() + dialogTitle.slice(1)} + {renderStatusBadge()}
- {dialogDescription}
- {/* Content */} -
- {/* Custom User Variables Section */} - {})} - isSubmitting={isSubmitting} - /> -
+ {/* Custom User Variables Section */} + {})} + isSubmitting={isSubmitting} + /> {/* Server Initialization Section */} -
- +
+
@@ -110,8 +111,8 @@ function InitializingStatusIcon({ serverName, onCancel, canCancel }: Initializin return (
-
@@ -121,8 +122,8 @@ function InitializingStatusIcon({ serverName, onCancel, canCancel }: Initializin function ConnectingStatusIcon({ serverName }: StatusIconProps) { return (
-
diff --git a/client/src/components/MCP/ServerInitializationSection.tsx b/client/src/components/MCP/ServerInitializationSection.tsx index 0623ba1a2..5d793921e 100644 --- a/client/src/components/MCP/ServerInitializationSection.tsx +++ b/client/src/components/MCP/ServerInitializationSection.tsx @@ -1,23 +1,24 @@ -import React, { useCallback } from 'react'; -import { Button } from '@librechat/client'; -import { RefreshCw, Link } from 'lucide-react'; +import React from 'react'; +import { RefreshCw } from 'lucide-react'; +import { Button, Spinner } from '@librechat/client'; import { useMCPServerManager } from '~/hooks/MCP/useMCPServerManager'; import { useLocalize } from '~/hooks'; interface ServerInitializationSectionProps { + sidePanel?: boolean; serverName: string; requiresOAuth: boolean; hasCustomUserVars?: boolean; } export default function ServerInitializationSection({ + sidePanel = false, serverName, requiresOAuth, hasCustomUserVars = false, }: ServerInitializationSectionProps) { const localize = useLocalize(); - // Use the centralized server manager instead of the old initialization hook so we can handle multiple oauth flows at once const { initializeServer, connectionStatus, @@ -33,99 +34,66 @@ export default function ServerInitializationSection({ const isServerInitializing = isInitializing(serverName); const serverOAuthUrl = getOAuthUrl(serverName); - const handleInitializeClick = useCallback(() => { - initializeServer(serverName, false); - }, [initializeServer, serverName]); + const shouldShowReinit = isConnected && (requiresOAuth || hasCustomUserVars); + const shouldShowInit = !isConnected && !serverOAuthUrl; - const handleCancelClick = useCallback(() => { - cancelOAuthFlow(serverName); - }, [cancelOAuthFlow, serverName]); - - if (isConnected && (requiresOAuth || hasCustomUserVars)) { - return ( -
- -
- ); - } - - if (isConnected) { + if (!shouldShowReinit && !shouldShowInit && !serverOAuthUrl) { return null; } - return ( -
-
+ if (serverOAuthUrl) { + return ( + <>
- - {requiresOAuth - ? localize('com_ui_mcp_not_authenticated', { 0: serverName }) - : localize('com_ui_mcp_not_initialized', { 0: serverName })} - -
- {/* Only show authenticate button when OAuth URL is not present */} - {!serverOAuthUrl && ( + - )} -
- - {/* OAuth URL display */} - {serverOAuthUrl && ( -
-
-
- -
- - {localize('com_ui_auth_url')} - -
-
- - -
-

- {localize('com_ui_oauth_flow_desc')} -

- )} + + ); + } + + // Unified button rendering + const isReinit = shouldShowReinit; + const outerClass = isReinit ? 'flex justify-start' : 'flex justify-end'; + const buttonVariant = isReinit ? undefined : 'default'; + const buttonText = isServerInitializing + ? localize('com_ui_loading') + : isReinit + ? localize('com_ui_reinitialize') + : requiresOAuth + ? localize('com_ui_authenticate') + : localize('com_ui_mcp_initialize'); + const icon = isServerInitializing ? ( + + ) : ( + + ); + + return ( +
+
); } diff --git a/client/src/components/SidePanel/MCP/MCPPanel.tsx b/client/src/components/SidePanel/MCP/MCPPanel.tsx index c67517278..b90efa197 100644 --- a/client/src/components/SidePanel/MCP/MCPPanel.tsx +++ b/client/src/components/SidePanel/MCP/MCPPanel.tsx @@ -6,8 +6,8 @@ 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 { useMCPConnectionStatusQuery } from '~/data-provider/Tools/queries'; +import CustomUserVarsSection from '~/components/MCP/CustomUserVarsSection'; import BadgeRowProvider from '~/Providers/BadgeRowContext'; import { useGetStartupConfig } from '~/data-provider'; import MCPPanelSkeleton from './MCPPanelSkeleton'; @@ -127,20 +127,12 @@ function MCPPanelContent() { const serverStatus = connectionStatus[selectedServerNameForEditing]; return ( -
- -

- {localize('com_sidepanel_mcp_variables_for', { '0': serverBeingEdited.serverName })} -

-
+
{mcpServerDefinitions.map((server) => { const serverStatus = connectionStatus[server.serverName]; @@ -189,7 +182,7 @@ function MCPPanelContent() { {server.serverName} {serverStatus && ( (({ className, ...props }, ref) => ( ));