Skip to content

Commit 3b0217f

Browse files
dustinhealydanny-avila
authored andcommitted
🔑 refactor: MCP Settings Rendering Logic for OAuth Servers (danny-avila#8718)
* feat: add OAuth servers to conditional rendering logic for MCPPanel in SideNav * feat: add startup flag check to conditional rendering logic * fix: correct improper handling of failure state in reinitialize endpoint * fix: change MCP config components to better handle servers without customUserVars - removes the subtle reinitialize button from config components of servers without customUserVars or OAuth - adds a placeholder message for components where servers have no customUserVars configured * style: swap CustomUserVarsSection and ServerInitializationSection positions * style: fix coloring for light mode and align more with existing design patterns * chore: remove extraneous comments * chore: reorder imports and `isEnabled` from api package --------- Co-authored-by: Danny Avila <[email protected]>
1 parent 560c003 commit 3b0217f

File tree

10 files changed

+68
-33
lines changed

10 files changed

+68
-33
lines changed

api/server/routes/config.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
const express = require('express');
2+
const { isEnabled } = require('@librechat/api');
23
const { logger } = require('@librechat/data-schemas');
34
const { CacheKeys, defaultSocialLogins, Constants } = require('librechat-data-provider');
45
const { getCustomConfig } = require('~/server/services/Config/getCustomConfig');
56
const { getLdapConfig } = require('~/server/services/Config/ldap');
67
const { getProjectByName } = require('~/models/Project');
7-
const { isEnabled } = require('~/server/utils');
8+
const { getMCPManager } = require('~/config');
89
const { getLogStores } = require('~/cache');
910

1011
const router = express.Router();
@@ -102,11 +103,16 @@ router.get('/', async function (req, res) {
102103
payload.mcpServers = {};
103104
const config = await getCustomConfig();
104105
if (config?.mcpServers != null) {
106+
const mcpManager = getMCPManager();
107+
const oauthServers = mcpManager.getOAuthServers();
108+
105109
for (const serverName in config.mcpServers) {
106110
const serverConfig = config.mcpServers[serverName];
107111
payload.mcpServers[serverName] = {
108112
customUserVars: serverConfig?.customUserVars || {},
109113
chatMenu: serverConfig?.chatMenu,
114+
isOAuth: oauthServers.has(serverName),
115+
startup: serverConfig?.startup,
110116
};
111117
}
112118
}

api/server/routes/mcp.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -452,11 +452,19 @@ router.post('/:serverName/reinitialize', requireJwtAuth, async (req, res) => {
452452
`[MCP Reinitialize] Sending response for ${serverName} - oauthRequired: ${oauthRequired}, oauthUrl: ${oauthUrl ? 'present' : 'null'}`,
453453
);
454454

455+
const getResponseMessage = () => {
456+
if (oauthRequired) {
457+
return `MCP server '${serverName}' ready for OAuth authentication`;
458+
}
459+
if (userConnection) {
460+
return `MCP server '${serverName}' reinitialized successfully`;
461+
}
462+
return `Failed to reinitialize MCP server '${serverName}'`;
463+
};
464+
455465
res.json({
456-
success: true,
457-
message: oauthRequired
458-
? `MCP server '${serverName}' ready for OAuth authentication`
459-
: `MCP server '${serverName}' reinitialized successfully`,
466+
success: userConnection && !oauthRequired,
467+
message: getResponseMessage(),
460468
serverName,
461469
oauthRequired,
462470
oauthUrl,

client/src/components/MCP/CustomUserVarsSection.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,15 @@ export default function CustomUserVarsSection({
110110

111111
const handleRevokeClick = () => {
112112
onRevoke();
113-
// Reset form after revoke
114113
reset();
115114
};
116115

117-
// Don't render if no fields to configure
118116
if (!fields || Object.keys(fields).length === 0) {
119-
return null;
117+
return (
118+
<div className="p-4 text-center text-sm text-gray-500">
119+
{localize('com_sidepanel_mcp_no_custom_vars', { '0': serverName })}
120+
</div>
121+
);
120122
}
121123

122124
return (

client/src/components/MCP/MCPConfigDialog.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ export default function MCPConfigDialog({
132132
<ServerInitializationSection
133133
serverName={serverName}
134134
requiresOAuth={serverStatus?.requiresOAuth || false}
135+
hasCustomUserVars={fieldsSchema && Object.keys(fieldsSchema).length > 0}
135136
/>
136137
</OGDialogContent>
137138
</OGDialog>

client/src/components/MCP/ServerInitializationSection.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import { useLocalize } from '~/hooks';
77
interface ServerInitializationSectionProps {
88
serverName: string;
99
requiresOAuth: boolean;
10+
hasCustomUserVars?: boolean;
1011
}
1112

1213
export default function ServerInitializationSection({
1314
serverName,
1415
requiresOAuth,
16+
hasCustomUserVars = false,
1517
}: ServerInitializationSectionProps) {
1618
const localize = useLocalize();
1719

@@ -39,8 +41,7 @@ export default function ServerInitializationSection({
3941
cancelOAuthFlow(serverName);
4042
}, [cancelOAuthFlow, serverName]);
4143

42-
// Show subtle reinitialize option if connected
43-
if (isConnected) {
44+
if (isConnected && (requiresOAuth || hasCustomUserVars)) {
4445
return (
4546
<div className="flex justify-start">
4647
<button
@@ -55,11 +56,15 @@ export default function ServerInitializationSection({
5556
);
5657
}
5758

59+
if (isConnected) {
60+
return null;
61+
}
62+
5863
return (
59-
<div className="rounded-lg border border-[#991b1b] bg-[#2C1315] p-4">
64+
<div className="rounded-lg border border-amber-200 bg-amber-50 p-4 dark:border-amber-700 dark:bg-amber-900/20">
6065
<div className="flex items-center justify-between">
6166
<div className="flex items-center gap-2">
62-
<span className="text-sm font-medium text-red-700 dark:text-red-300">
67+
<span className="text-sm font-medium text-amber-800 dark:text-amber-200">
6368
{requiresOAuth
6469
? localize('com_ui_mcp_not_authenticated', { 0: serverName })
6570
: localize('com_ui_mcp_not_initialized', { 0: serverName })}
@@ -70,7 +75,7 @@ export default function ServerInitializationSection({
7075
<Button
7176
onClick={handleInitializeClick}
7277
disabled={isServerInitializing}
73-
className="flex items-center gap-2 bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 dark:hover:bg-blue-800"
78+
className="btn btn-primary focus:shadow-outline flex w-full items-center justify-center px-4 py-2 font-semibold text-white hover:bg-green-600 focus:border-green-500"
7479
>
7580
{isServerInitializing ? (
7681
<>
@@ -103,7 +108,7 @@ export default function ServerInitializationSection({
103108
<div className="flex items-center gap-2">
104109
<Button
105110
onClick={() => window.open(serverOAuthUrl, '_blank', 'noopener,noreferrer')}
106-
className="flex-1 bg-blue-600 text-white hover:bg-blue-700 dark:hover:bg-blue-800"
111+
className="flex-1 bg-green-600 text-white hover:bg-green-700 dark:hover:bg-green-800"
107112
>
108113
{localize('com_ui_continue_oauth')}
109114
</Button>

client/src/components/SidePanel/MCP/MCPPanel.tsx

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -141,29 +141,31 @@ function MCPPanelContent() {
141141
{localize('com_sidepanel_mcp_variables_for', { '0': serverBeingEdited.serverName })}
142142
</h3>
143143

144-
{/* Server Initialization Section */}
145144
<div className="mb-4">
146-
<ServerInitializationSection
145+
<CustomUserVarsSection
147146
serverName={selectedServerNameForEditing}
148-
requiresOAuth={serverStatus?.requiresOAuth || false}
147+
fields={serverBeingEdited.config.customUserVars}
148+
onSave={(authData) => {
149+
if (selectedServerNameForEditing) {
150+
handleConfigSave(selectedServerNameForEditing, authData);
151+
}
152+
}}
153+
onRevoke={() => {
154+
if (selectedServerNameForEditing) {
155+
handleConfigRevoke(selectedServerNameForEditing);
156+
}
157+
}}
158+
isSubmitting={updateUserPluginsMutation.isLoading}
149159
/>
150160
</div>
151161

152-
{/* Custom User Variables Section */}
153-
<CustomUserVarsSection
162+
<ServerInitializationSection
154163
serverName={selectedServerNameForEditing}
155-
fields={serverBeingEdited.config.customUserVars}
156-
onSave={(authData) => {
157-
if (selectedServerNameForEditing) {
158-
handleConfigSave(selectedServerNameForEditing, authData);
159-
}
160-
}}
161-
onRevoke={() => {
162-
if (selectedServerNameForEditing) {
163-
handleConfigRevoke(selectedServerNameForEditing);
164-
}
165-
}}
166-
isSubmitting={updateUserPluginsMutation.isLoading}
164+
requiresOAuth={serverStatus?.requiresOAuth || false}
165+
hasCustomUserVars={
166+
serverBeingEdited.config.customUserVars &&
167+
Object.keys(serverBeingEdited.config.customUserVars).length > 0
168+
}
167169
/>
168170
</div>
169171
);

client/src/hooks/MCP/useMCPServerManager.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,12 @@ export function useMCPServerManager() {
234234

235235
cleanupServerState(serverName);
236236
}
237+
} else {
238+
showToast({
239+
message: localize('com_ui_mcp_init_failed', { 0: serverName }),
240+
status: 'error',
241+
});
242+
cleanupServerState(serverName);
237243
}
238244
} catch (error) {
239245
console.error(`[MCP Manager] Failed to initialize ${serverName}:`, error);

client/src/hooks/Nav/useSideNavLinks.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,10 @@ export default function useSideNavLinks({
155155
if (
156156
startupConfig?.mcpServers &&
157157
Object.values(startupConfig.mcpServers).some(
158-
(server) => server.customUserVars && Object.keys(server.customUserVars).length > 0,
158+
(server: any) =>
159+
(server.customUserVars && Object.keys(server.customUserVars).length > 0) ||
160+
server.isOAuth ||
161+
server.startup === false,
159162
)
160163
) {
161164
links.push({

client/src/locales/en/translation.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,7 @@
506506
"com_sidepanel_hide_panel": "Hide Panel",
507507
"com_sidepanel_manage_files": "Manage Files",
508508
"com_sidepanel_mcp_no_servers_with_vars": "No MCP servers with configurable variables.",
509+
"com_sidepanel_mcp_no_custom_vars": "No custom user variables set for {{0}}",
509510
"com_sidepanel_mcp_variables_for": "MCP Variables for {{0}}",
510511
"com_sidepanel_parameters": "Parameters",
511512
"com_sources_image_alt": "Search result image",
@@ -851,7 +852,6 @@
851852
"com_ui_mcp_authenticated_success": "MCP server '{{0}}' authenticated successfully",
852853
"com_ui_mcp_dialog_desc": "Please enter the necessary information below.",
853854
"com_ui_mcp_enter_var": "Enter value for {{0}}",
854-
"com_ui_mcp_init_cancelled": "MCP server '{{0}}' initialization was cancelled due to simultaneous request",
855855
"com_ui_mcp_init_failed": "Failed to initialize MCP server",
856856
"com_ui_mcp_initialize": "Initialize",
857857
"com_ui_mcp_initialized_success": "MCP server '{{0}}' initialized successfully",

packages/data-provider/src/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,8 @@ export type TStartupConfig = {
613613
}
614614
>;
615615
chatMenu?: boolean;
616+
isOAuth?: boolean;
617+
startup?: boolean;
616618
}
617619
>;
618620
mcpPlaceholder?: string;

0 commit comments

Comments
 (0)