🧹 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 { 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 type { MCPServerStatus } from 'librechat-data-provider';
import { useLocalize } from '~/hooks';
@ -17,6 +17,7 @@ interface MCPCardActionsProps {
onConfigClick: (e: React.MouseEvent) => void;
onInitialize: () => void;
onCancel: (e: React.MouseEvent) => void;
onRevoke?: () => void;
}
/**
@ -26,6 +27,7 @@ interface MCPCardActionsProps {
* - Pencil: Edit server definition (Settings panel only)
* - PlugZap: Connect/Authenticate (for disconnected/error servers)
* - SlidersHorizontal: Configure custom variables (for connected servers with vars)
* - Trash2: Revoke OAuth access (for connected OAuth servers)
* - RefreshCw: Reconnect/Refresh (for connected servers)
* - Spinner: Loading state (with X on hover for cancel)
*/
@ -41,6 +43,7 @@ export default function MCPCardActions({
onConfigClick,
onInitialize,
onCancel,
onRevoke,
}: MCPCardActionsProps) {
const localize = useLocalize();
@ -162,6 +165,20 @@ export default function MCPCardActions({
<RefreshCw className="size-3.5" aria-hidden="true" />
</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>
);
}

View file

@ -30,7 +30,7 @@ export default function MCPServerCard({
}: MCPServerCardProps) {
const localize = useLocalize();
const triggerRef = useRef<HTMLDivElement>(null);
const { initializeServer } = useMCPServerManager();
const { initializeServer, revokeOAuthForServer } = useMCPServerManager();
const [dialogOpen, setDialogOpen] = useState(false);
const statusIconProps = getServerStatusIconProps(server.serverName);
@ -50,9 +50,20 @@ export default function MCPServerCard({
const canEdit = canCreateEditMCPs && canEditThisServer;
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);
};
const handleRevoke = () => {
revokeOAuthForServer(server.serverName);
};
const handleEditClick = (e: React.MouseEvent) => {
e.stopPropagation();
e.preventDefault();
@ -130,6 +141,7 @@ export default function MCPServerCard({
onConfigClick={onConfigClick}
onInitialize={handleInitialize}
onCancel={onCancel}
onRevoke={handleRevoke}
/>
</div>
</div>

View file

@ -94,8 +94,20 @@ export function useMCPServerManager({ conversationId }: { conversationId?: strin
const cancelOAuthMutation = useCancelMCPOAuthMutation();
const updateUserPluginsMutation = useUpdateUserPluginsMutation({
onSuccess: async () => {
showToast({ message: localize('com_nav_mcp_vars_updated'), status: 'success' });
onSuccess: async (_data, variables) => {
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([
queryClient.invalidateQueries([QueryKeys.mcpServers]),
@ -491,13 +503,10 @@ export function useMCPServerManager({ conversationId }: { conversationId?: strin
auth: {},
};
updateUserPluginsMutation.mutate(payload);
const currentValues = mcpValues ?? [];
const filteredValues = currentValues.filter((name) => name !== targetName);
setMCPValues(filteredValues);
/** Deselection is now handled centrally in updateUserPluginsMutation.onSuccess */
}
},
[selectedToolForConfig, updateUserPluginsMutation, mcpValues, setMCPValues],
[selectedToolForConfig, updateUserPluginsMutation],
);
/** 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_vars_update_error": "Error updating MCP custom user variables",
"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_my_files": "My Files",
"com_nav_not_supported": "Not Supported",