diff --git a/api/server/routes/config.js b/api/server/routes/config.js index 55d4cc3067..bd1b0b12c3 100644 --- a/api/server/routes/config.js +++ b/api/server/routes/config.js @@ -1,10 +1,11 @@ const express = require('express'); +const { isEnabled } = require('@librechat/api'); const { logger } = require('@librechat/data-schemas'); const { CacheKeys, defaultSocialLogins, Constants } = require('librechat-data-provider'); const { getCustomConfig } = require('~/server/services/Config/getCustomConfig'); const { getLdapConfig } = require('~/server/services/Config/ldap'); const { getProjectByName } = require('~/models/Project'); -const { isEnabled } = require('~/server/utils'); +const { getMCPManager } = require('~/config'); const { getLogStores } = require('~/cache'); const router = express.Router(); @@ -102,11 +103,16 @@ router.get('/', async function (req, res) { payload.mcpServers = {}; const config = await getCustomConfig(); if (config?.mcpServers != null) { + const mcpManager = getMCPManager(); + const oauthServers = mcpManager.getOAuthServers(); + for (const serverName in config.mcpServers) { const serverConfig = config.mcpServers[serverName]; payload.mcpServers[serverName] = { customUserVars: serverConfig?.customUserVars || {}, chatMenu: serverConfig?.chatMenu, + isOAuth: oauthServers.has(serverName), + startup: serverConfig?.startup, }; } } diff --git a/api/server/routes/mcp.js b/api/server/routes/mcp.js index 35bba77ae6..f66d671a87 100644 --- a/api/server/routes/mcp.js +++ b/api/server/routes/mcp.js @@ -452,11 +452,19 @@ router.post('/:serverName/reinitialize', requireJwtAuth, async (req, res) => { `[MCP Reinitialize] Sending response for ${serverName} - oauthRequired: ${oauthRequired}, oauthUrl: ${oauthUrl ? 'present' : 'null'}`, ); + const getResponseMessage = () => { + if (oauthRequired) { + return `MCP server '${serverName}' ready for OAuth authentication`; + } + if (userConnection) { + return `MCP server '${serverName}' reinitialized successfully`; + } + return `Failed to reinitialize MCP server '${serverName}'`; + }; + res.json({ - success: true, - message: oauthRequired - ? `MCP server '${serverName}' ready for OAuth authentication` - : `MCP server '${serverName}' reinitialized successfully`, + success: userConnection && !oauthRequired, + message: getResponseMessage(), serverName, oauthRequired, oauthUrl, diff --git a/client/src/components/MCP/CustomUserVarsSection.tsx b/client/src/components/MCP/CustomUserVarsSection.tsx index 1d6060c914..c178fae24c 100644 --- a/client/src/components/MCP/CustomUserVarsSection.tsx +++ b/client/src/components/MCP/CustomUserVarsSection.tsx @@ -110,13 +110,15 @@ export default function CustomUserVarsSection({ const handleRevokeClick = () => { onRevoke(); - // Reset form after revoke reset(); }; - // Don't render if no fields to configure if (!fields || Object.keys(fields).length === 0) { - return null; + return ( +
+ {localize('com_sidepanel_mcp_no_custom_vars', { '0': serverName })} +
+ ); } return ( diff --git a/client/src/components/MCP/MCPConfigDialog.tsx b/client/src/components/MCP/MCPConfigDialog.tsx index 1ad0ea7b63..5bcd590ec7 100644 --- a/client/src/components/MCP/MCPConfigDialog.tsx +++ b/client/src/components/MCP/MCPConfigDialog.tsx @@ -132,6 +132,7 @@ export default function MCPConfigDialog({ 0} /> diff --git a/client/src/components/MCP/ServerInitializationSection.tsx b/client/src/components/MCP/ServerInitializationSection.tsx index 36c9ca6b17..2113a9f844 100644 --- a/client/src/components/MCP/ServerInitializationSection.tsx +++ b/client/src/components/MCP/ServerInitializationSection.tsx @@ -7,11 +7,13 @@ import { useLocalize } from '~/hooks'; interface ServerInitializationSectionProps { serverName: string; requiresOAuth: boolean; + hasCustomUserVars?: boolean; } export default function ServerInitializationSection({ serverName, requiresOAuth, + hasCustomUserVars = false, }: ServerInitializationSectionProps) { const localize = useLocalize(); @@ -39,8 +41,7 @@ export default function ServerInitializationSection({ cancelOAuthFlow(serverName); }, [cancelOAuthFlow, serverName]); - // Show subtle reinitialize option if connected - if (isConnected) { + if (isConnected && (requiresOAuth || hasCustomUserVars)) { return (
diff --git a/client/src/components/SidePanel/MCP/MCPPanel.tsx b/client/src/components/SidePanel/MCP/MCPPanel.tsx index a948f5f656..c67517278c 100644 --- a/client/src/components/SidePanel/MCP/MCPPanel.tsx +++ b/client/src/components/SidePanel/MCP/MCPPanel.tsx @@ -141,29 +141,31 @@ function MCPPanelContent() { {localize('com_sidepanel_mcp_variables_for', { '0': serverBeingEdited.serverName })} - {/* Server Initialization Section */}
- { + if (selectedServerNameForEditing) { + handleConfigSave(selectedServerNameForEditing, authData); + } + }} + onRevoke={() => { + if (selectedServerNameForEditing) { + handleConfigRevoke(selectedServerNameForEditing); + } + }} + isSubmitting={updateUserPluginsMutation.isLoading} />
- {/* Custom User Variables Section */} - { - if (selectedServerNameForEditing) { - handleConfigSave(selectedServerNameForEditing, authData); - } - }} - onRevoke={() => { - if (selectedServerNameForEditing) { - handleConfigRevoke(selectedServerNameForEditing); - } - }} - isSubmitting={updateUserPluginsMutation.isLoading} + requiresOAuth={serverStatus?.requiresOAuth || false} + hasCustomUserVars={ + serverBeingEdited.config.customUserVars && + Object.keys(serverBeingEdited.config.customUserVars).length > 0 + } />
); diff --git a/client/src/hooks/MCP/useMCPServerManager.ts b/client/src/hooks/MCP/useMCPServerManager.ts index b68feade3e..71ffa471b4 100644 --- a/client/src/hooks/MCP/useMCPServerManager.ts +++ b/client/src/hooks/MCP/useMCPServerManager.ts @@ -234,6 +234,12 @@ export function useMCPServerManager() { cleanupServerState(serverName); } + } else { + showToast({ + message: localize('com_ui_mcp_init_failed', { 0: serverName }), + status: 'error', + }); + cleanupServerState(serverName); } } catch (error) { console.error(`[MCP Manager] Failed to initialize ${serverName}:`, error); diff --git a/client/src/hooks/Nav/useSideNavLinks.ts b/client/src/hooks/Nav/useSideNavLinks.ts index 7345415215..93f96de068 100644 --- a/client/src/hooks/Nav/useSideNavLinks.ts +++ b/client/src/hooks/Nav/useSideNavLinks.ts @@ -155,7 +155,10 @@ export default function useSideNavLinks({ if ( startupConfig?.mcpServers && Object.values(startupConfig.mcpServers).some( - (server) => server.customUserVars && Object.keys(server.customUserVars).length > 0, + (server: any) => + (server.customUserVars && Object.keys(server.customUserVars).length > 0) || + server.isOAuth || + server.startup === false, ) ) { links.push({ diff --git a/client/src/locales/en/translation.json b/client/src/locales/en/translation.json index 964abab238..23ea0e26cc 100644 --- a/client/src/locales/en/translation.json +++ b/client/src/locales/en/translation.json @@ -506,6 +506,7 @@ "com_sidepanel_hide_panel": "Hide Panel", "com_sidepanel_manage_files": "Manage Files", "com_sidepanel_mcp_no_servers_with_vars": "No MCP servers with configurable variables.", + "com_sidepanel_mcp_no_custom_vars": "No custom user variables set for {{0}}", "com_sidepanel_mcp_variables_for": "MCP Variables for {{0}}", "com_sidepanel_parameters": "Parameters", "com_sources_image_alt": "Search result image", @@ -851,7 +852,6 @@ "com_ui_mcp_authenticated_success": "MCP server '{{0}}' authenticated successfully", "com_ui_mcp_dialog_desc": "Please enter the necessary information below.", "com_ui_mcp_enter_var": "Enter value for {{0}}", - "com_ui_mcp_init_cancelled": "MCP server '{{0}}' initialization was cancelled due to simultaneous request", "com_ui_mcp_init_failed": "Failed to initialize MCP server", "com_ui_mcp_initialize": "Initialize", "com_ui_mcp_initialized_success": "MCP server '{{0}}' initialized successfully", diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts index 89a3aa1383..4d53fba805 100644 --- a/packages/data-provider/src/config.ts +++ b/packages/data-provider/src/config.ts @@ -613,6 +613,8 @@ export type TStartupConfig = { } >; chatMenu?: boolean; + isOAuth?: boolean; + startup?: boolean; } >; mcpPlaceholder?: string;