diff --git a/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/AuthSection.tsx b/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/AuthSection.tsx index fb31c95949..216a68151e 100644 --- a/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/AuthSection.tsx +++ b/client/src/components/SidePanel/MCPBuilder/MCPServerDialog/sections/AuthSection.tsx @@ -3,9 +3,10 @@ import { Copy, CopyCheck } from 'lucide-react'; import { useFormContext, useWatch } from 'react-hook-form'; import { Permissions, PermissionTypes } from 'librechat-data-provider'; import { Label, Input, Checkbox, SecretInput, Radio, useToastContext } from '@librechat/client'; -import { AuthTypeEnum, AuthorizationTypeEnum } from '../hooks/useMCPServerForm'; import type { MCPServerFormData } from '../hooks/useMCPServerForm'; +import { AuthTypeEnum, AuthorizationTypeEnum } from '../hooks/useMCPServerForm'; import { useLocalize, useCopyToClipboard, useHasAccess } from '~/hooks'; +import { Collapse } from '~/components/ui'; import { cn } from '~/utils'; interface AuthSectionProps { @@ -83,7 +84,7 @@ export default function AuthSection({ isEditMode, serverName }: AuthSectionProps ); return ( -
+
{/* Auth Type Radio */}
@@ -102,78 +103,82 @@ export default function AuthSection({ isEditMode, serverName }: AuthSectionProps
{/* API Key Fields */} - {authType === AuthTypeEnum.ServiceHttp && ( +
- {/* User provides own key checkbox */} -
- - setValue('auth.api_key_source', checked ? 'user' : 'admin') - } - aria-labelledby="user_provides_key_label" - /> - + {/* User provides own key checkbox + admin-provided key */} +
+
+ + setValue('auth.api_key_source', checked ? 'user' : 'admin') + } + aria-labelledby="user_provides_key_label" + /> + +
+ + {/* API Key input - only when admin provides */} + +
+ + +
+
- {/* API Key input - only when admin provides */} - {apiKeySource !== 'user' && ( -
- - +
+ + + + + setValue('auth.api_key_authorization_type', val as AuthorizationTypeEnum) + } + fullWidth + aria-labelledby="header-format-label" /> -
- )} + - {/* Header Format Radio */} -
- - - - - setValue('auth.api_key_authorization_type', val as AuthorizationTypeEnum) - } - fullWidth - aria-labelledby="header-format-label" - /> -
- - {/* Custom header name */} - {authorizationType === AuthorizationTypeEnum.Custom && ( -
- - -
- )} + {/* Custom header name */} + +
+ + +
+
+
- )} +
{/* OAuth Fields */} - {authType === AuthTypeEnum.OAuth && ( +
{/* Client ID & Secret in a grid */}
@@ -200,7 +205,9 @@ export default function AuthSection({ isEditMode, serverName }: AuthSectionProps aria-describedby={ errors.auth?.oauth_client_id ? 'oauth-client-id-error' : undefined } - {...register('auth.oauth_client_id', { required: !isEditMode })} + {...register('auth.oauth_client_id', { + required: !isEditMode && authType === AuthTypeEnum.OAuth, + })} className={cn(errors.auth?.oauth_client_id && 'border-border-destructive')} /> {errors.auth?.oauth_client_id && ( @@ -288,10 +295,10 @@ export default function AuthSection({ isEditMode, serverName }: AuthSectionProps
)}
- )} +
{/* OBO Fields */} - {authType === AuthTypeEnum.OBO && ( +
- )} +
); } diff --git a/client/src/components/ui/Collapse.tsx b/client/src/components/ui/Collapse.tsx index bffa300389..9ddcb69155 100644 --- a/client/src/components/ui/Collapse.tsx +++ b/client/src/components/ui/Collapse.tsx @@ -12,12 +12,14 @@ interface CollapseProps { * the OAuth QR. The parent's content-driven height follows the tween in a single * motion, so stacked collapses (e.g. a loading skeleton swapping to a list) can * cross-fade smoothly without a measuring wrapper fighting nested reveals. - * Content fades to soften the swap and is hidden from assistive tech while closed. + * Content fades to soften the swap; while closed it is `inert` (removed from tab + * order and the a11y tree) so collapsed form fields can't be focused or read. */ export default function Collapse({ open, children, className }: CollapseProps) { return (