mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-03-01 22:00:18 +01:00
🧹 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:
parent
fc6f127b21
commit
90521bfb4e
4 changed files with 48 additions and 9 deletions
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue