LibreChat/client/src/components/MCP/ServerInitializationSection.tsx
Danny Avila 394bb6242b
📦 chore: Bump MCP SDK: Fix Types and MCP OAuth due to Update (#10811)
* chore: Bump @modelcontextprotocol/sdk to version 1.24.3

* refactor: Update resource handling in MCP parsers and types

- Simplified resource text checks in `parseAsString` and `formatToolContent` functions to ensure proper existence checks.
- Removed unnecessary resource name and description handling to streamline output.
- Updated type definitions in `index.ts` to align with the new structure from `@modelcontextprotocol/sdk`, enhancing type safety and clarity.
- Added `logo_uri` and `tos_uri` properties to `MCPOAuthHandler` for improved OAuth metadata support.

* refactor: Update custom endpoint configurations and type definitions

- Removed unused type imports and streamlined the custom parameters handling in `loadCustomEndpointsConfig`.
- Adjusted the `TCustomEndpointsConfig` type to utilize `TConfig` instead of `TEndpoint`, enhancing type accuracy.
- Made the endpoint schema optional in the configuration to improve flexibility.

* fix: Implement token cleanup and error handling for invalid OAuth tokens

- Added `cleanupInvalidTokens` method to remove invalid OAuth tokens from storage when detected.
- Introduced `isInvalidTokenError` method to identify errors indicating revoked or expired tokens.
- Integrated token cleanup logic into the connection attempt process to ensure fresh OAuth flow on invalid token detection.

* feat: Add revoke OAuth functionality in Server Initialization

- Introduced a new button to revoke OAuth for servers, enhancing user control over OAuth permissions.
- Updated the `useMCPServerManager` hook to include a standalone `revokeOAuthForServer` function for managing OAuth revocation.
- Adjusted the UI to conditionally render the revoke button based on server requirements.

* fix: error handling for authentication in MCPConnection

- Updated the error handling logic in MCPConnection to better identify various authentication error indicators, including 401 status, invalid tokens, and unauthorized messages.
- Removed the deprecated cleanupInvalidTokens method and integrated its logic into the connection attempt process for improved clarity and efficiency.
- Adjusted the MCPConnectionFactory to streamline the connection attempt process and handle OAuth errors more effectively.

* refactor: Update button rendering in ServerInitializationSection

- Removed the existing button for server initialization and replaced it with a new button implementation, maintaining the same functionality.
- Ensured consistent rendering of the button within the component's layout.

* chore: update resource type usage in parsers.test.ts
2025-12-11 16:38:40 -05:00

120 lines
3.3 KiB
TypeScript

import React from 'react';
import { RefreshCw, Trash2 } from 'lucide-react';
import { Button, Spinner } from '@librechat/client';
import { useLocalize, useMCPServerManager, useMCPConnectionStatus } from '~/hooks';
interface ServerInitializationSectionProps {
sidePanel?: boolean;
serverName: string;
requiresOAuth: boolean;
hasCustomUserVars?: boolean;
conversationId?: string | null;
}
export default function ServerInitializationSection({
serverName,
requiresOAuth,
conversationId,
sidePanel = false,
hasCustomUserVars = false,
}: ServerInitializationSectionProps) {
const localize = useLocalize();
const {
getOAuthUrl,
isCancellable,
isInitializing,
cancelOAuthFlow,
initializeServer,
availableMCPServers,
revokeOAuthForServer,
} = useMCPServerManager({ conversationId });
const { connectionStatus } = useMCPConnectionStatus({
enabled: !!availableMCPServers && availableMCPServers.length > 0,
});
const serverStatus = connectionStatus?.[serverName];
const isConnected = serverStatus?.connectionState === 'connected';
const canCancel = isCancellable(serverName);
const isServerInitializing = isInitializing(serverName);
const serverOAuthUrl = getOAuthUrl(serverName);
const shouldShowReinit = isConnected && (requiresOAuth || hasCustomUserVars);
const shouldShowInit = !isConnected && !serverOAuthUrl;
if (!shouldShowReinit && !shouldShowInit && !serverOAuthUrl) {
return null;
}
if (serverOAuthUrl) {
return (
<>
<div className="flex items-center gap-2">
<Button
onClick={() => cancelOAuthFlow(serverName)}
disabled={!canCancel}
variant="outline"
title={!canCancel ? 'disabled' : undefined}
>
{localize('com_ui_cancel')}
</Button>
<Button
variant="submit"
onClick={() => window.open(serverOAuthUrl, '_blank', 'noopener,noreferrer')}
className="flex-1"
>
{localize('com_ui_continue_oauth')}
</Button>
</div>
</>
);
}
// Unified button rendering
const isReinit = shouldShowReinit;
const buttonVariant = isReinit ? undefined : 'default';
let buttonText = '';
if (isServerInitializing) {
buttonText = localize('com_ui_loading');
} else if (isReinit) {
buttonText = localize('com_ui_reinitialize');
} else if (requiresOAuth) {
buttonText = localize('com_ui_authenticate');
} else {
buttonText = localize('com_ui_mcp_initialize');
}
const icon = isServerInitializing ? (
<Spinner className="h-4 w-4" />
) : (
<RefreshCw className="h-4 w-4" aria-hidden="true" />
);
return (
<div className="flex items-center gap-2">
{requiresOAuth && revokeOAuthForServer && (
<Button
size="sm"
variant="destructive"
onClick={() => revokeOAuthForServer(serverName)}
aria-label={localize('com_ui_revoke')}
>
<Trash2 className="h-4 w-4" />
{localize('com_ui_revoke')}
</Button>
)}
<Button
variant={buttonVariant}
onClick={() => initializeServer(serverName, false)}
disabled={isServerInitializing}
size={sidePanel ? 'sm' : 'default'}
className="flex-1"
>
{icon}
{buttonText}
</Button>
</div>
);
}