🧹 fix: MCP Panel Regressions after UI refactor (#11312)

* fix: Revoke OAuth and Vars. Config Regressions in MCP Panel

- Introduced a new Trash2 icon button in MCPCardActions for revoking OAuth access on connected OAuth servers.
- Updated MCPServerCard to handle the revoke action, allowing users to revoke OAuth for specific servers.
- Enhanced user experience by ensuring the revoke option is available regardless of the server's connection state.

* refactor: Reorganize Revoke Button Logic in MCPCardActions and Update Toast Messages

- Moved the Revoke button for OAuth servers to a new position in MCPCardActions for improved visibility.
- Updated the success message logic in useMCPServerManager to differentiate between uninstall and variable update actions, enhancing user feedback.

* i18n: Add new translation for MCP server access revocation message

* refactor: Centralize Deselection Logic in updateUserPluginsMutation

- Updated the success handler in useUpdateUserPluginsMutation to manage deselection of MCP server values when revoking access, improving code clarity and reducing redundancy.
- Simplified message assignment logic for user feedback during plugin updates.
This commit is contained in:
Danny Avila 2026-01-12 19:01:45 -05:00 committed by GitHub
parent fc6f127b21
commit 90521bfb4e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 48 additions and 9 deletions

View file

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Pencil, PlugZap, SlidersHorizontal, RefreshCw, X } from 'lucide-react'; import { Pencil, PlugZap, SlidersHorizontal, RefreshCw, X, Trash2 } from 'lucide-react';
import { Spinner, TooltipAnchor } from '@librechat/client'; import { Spinner, TooltipAnchor } from '@librechat/client';
import type { MCPServerStatus } from 'librechat-data-provider'; import type { MCPServerStatus } from 'librechat-data-provider';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';
@ -17,6 +17,7 @@ interface MCPCardActionsProps {
onConfigClick: (e: React.MouseEvent) => void; onConfigClick: (e: React.MouseEvent) => void;
onInitialize: () => void; onInitialize: () => void;
onCancel: (e: React.MouseEvent) => void; onCancel: (e: React.MouseEvent) => void;
onRevoke?: () => void;
} }
/** /**
@ -26,6 +27,7 @@ interface MCPCardActionsProps {
* - Pencil: Edit server definition (Settings panel only) * - Pencil: Edit server definition (Settings panel only)
* - PlugZap: Connect/Authenticate (for disconnected/error servers) * - PlugZap: Connect/Authenticate (for disconnected/error servers)
* - SlidersHorizontal: Configure custom variables (for connected servers with vars) * - SlidersHorizontal: Configure custom variables (for connected servers with vars)
* - Trash2: Revoke OAuth access (for connected OAuth servers)
* - RefreshCw: Reconnect/Refresh (for connected servers) * - RefreshCw: Reconnect/Refresh (for connected servers)
* - Spinner: Loading state (with X on hover for cancel) * - Spinner: Loading state (with X on hover for cancel)
*/ */
@ -41,6 +43,7 @@ export default function MCPCardActions({
onConfigClick, onConfigClick,
onInitialize, onInitialize,
onCancel, onCancel,
onRevoke,
}: MCPCardActionsProps) { }: MCPCardActionsProps) {
const localize = useLocalize(); const localize = useLocalize();
@ -162,6 +165,20 @@ export default function MCPCardActions({
<RefreshCw className="size-3.5" aria-hidden="true" /> <RefreshCw className="size-3.5" aria-hidden="true" />
</TooltipAnchor> </TooltipAnchor>
)} )}
{/* Revoke button - for OAuth servers (available regardless of connection state) */}
{serverStatus?.requiresOAuth && onRevoke && (
<TooltipAnchor
description={localize('com_ui_revoke')}
side="top"
className={cn(buttonBaseClass, 'text-red-500 hover:text-red-600')}
aria-label={localize('com_ui_revoke')}
role="button"
onClick={onRevoke}
>
<Trash2 className="size-3.5" aria-hidden="true" />
</TooltipAnchor>
)}
</div> </div>
); );
} }

View file

@ -30,7 +30,7 @@ export default function MCPServerCard({
}: MCPServerCardProps) { }: MCPServerCardProps) {
const localize = useLocalize(); const localize = useLocalize();
const triggerRef = useRef<HTMLDivElement>(null); const triggerRef = useRef<HTMLDivElement>(null);
const { initializeServer } = useMCPServerManager(); const { initializeServer, revokeOAuthForServer } = useMCPServerManager();
const [dialogOpen, setDialogOpen] = useState(false); const [dialogOpen, setDialogOpen] = useState(false);
const statusIconProps = getServerStatusIconProps(server.serverName); const statusIconProps = getServerStatusIconProps(server.serverName);
@ -50,9 +50,20 @@ export default function MCPServerCard({
const canEdit = canCreateEditMCPs && canEditThisServer; const canEdit = canCreateEditMCPs && canEditThisServer;
const handleInitialize = () => { const handleInitialize = () => {
/** If server has custom user vars and is not already connected, show config dialog first
* This ensures users can enter credentials before initialization attempts
*/
if (hasCustomUserVars && serverStatus?.connectionState !== 'connected') {
onConfigClick({ stopPropagation: () => {}, preventDefault: () => {} } as React.MouseEvent);
return;
}
initializeServer(server.serverName); initializeServer(server.serverName);
}; };
const handleRevoke = () => {
revokeOAuthForServer(server.serverName);
};
const handleEditClick = (e: React.MouseEvent) => { const handleEditClick = (e: React.MouseEvent) => {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
@ -130,6 +141,7 @@ export default function MCPServerCard({
onConfigClick={onConfigClick} onConfigClick={onConfigClick}
onInitialize={handleInitialize} onInitialize={handleInitialize}
onCancel={onCancel} onCancel={onCancel}
onRevoke={handleRevoke}
/> />
</div> </div>
</div> </div>

View file

@ -94,8 +94,20 @@ export function useMCPServerManager({ conversationId }: { conversationId?: strin
const cancelOAuthMutation = useCancelMCPOAuthMutation(); const cancelOAuthMutation = useCancelMCPOAuthMutation();
const updateUserPluginsMutation = useUpdateUserPluginsMutation({ const updateUserPluginsMutation = useUpdateUserPluginsMutation({
onSuccess: async () => { onSuccess: async (_data, variables) => {
showToast({ message: localize('com_nav_mcp_vars_updated'), status: 'success' }); const isRevoke = variables.action === 'uninstall';
const message = isRevoke
? localize('com_nav_mcp_access_revoked')
: localize('com_nav_mcp_vars_updated');
showToast({ message, status: 'success' });
/** Deselect server from mcpValues when revoking access */
if (isRevoke && variables.pluginKey?.startsWith(Constants.mcp_prefix)) {
const serverName = variables.pluginKey.replace(Constants.mcp_prefix, '');
const currentValues = mcpValuesRef.current ?? [];
const filteredValues = currentValues.filter((name) => name !== serverName);
setMCPValues(filteredValues);
}
await Promise.all([ await Promise.all([
queryClient.invalidateQueries([QueryKeys.mcpServers]), queryClient.invalidateQueries([QueryKeys.mcpServers]),
@ -491,13 +503,10 @@ export function useMCPServerManager({ conversationId }: { conversationId?: strin
auth: {}, auth: {},
}; };
updateUserPluginsMutation.mutate(payload); updateUserPluginsMutation.mutate(payload);
/** Deselection is now handled centrally in updateUserPluginsMutation.onSuccess */
const currentValues = mcpValues ?? [];
const filteredValues = currentValues.filter((name) => name !== targetName);
setMCPValues(filteredValues);
} }
}, },
[selectedToolForConfig, updateUserPluginsMutation, mcpValues, setMCPValues], [selectedToolForConfig, updateUserPluginsMutation],
); );
/** Standalone revoke function for OAuth servers - doesn't require selectedToolForConfig */ /** Standalone revoke function for OAuth servers - doesn't require selectedToolForConfig */

View file

@ -546,6 +546,7 @@
"com_nav_mcp_status_unknown": "Unknown", "com_nav_mcp_status_unknown": "Unknown",
"com_nav_mcp_vars_update_error": "Error updating MCP custom user variables", "com_nav_mcp_vars_update_error": "Error updating MCP custom user variables",
"com_nav_mcp_vars_updated": "MCP custom user variables updated successfully.", "com_nav_mcp_vars_updated": "MCP custom user variables updated successfully.",
"com_nav_mcp_access_revoked": "MCP server access revoked successfully.",
"com_nav_modular_chat": "Enable switching Endpoints mid-conversation", "com_nav_modular_chat": "Enable switching Endpoints mid-conversation",
"com_nav_my_files": "My Files", "com_nav_my_files": "My Files",
"com_nav_not_supported": "Not Supported", "com_nav_not_supported": "Not Supported",