diff --git a/api/server/controllers/mcp.js b/api/server/controllers/mcp.js index a50680f3d1..d58e1feacf 100644 --- a/api/server/controllers/mcp.js +++ b/api/server/controllers/mcp.js @@ -146,6 +146,7 @@ const getMCPTools = async (req, res) => { authField: key, label: value.title || key, description: value.description || '', + sensitive: value.sensitive, })); server.authenticated = false; } diff --git a/client/src/common/types.ts b/client/src/common/types.ts index d8f86f23f4..7be7bef749 100644 --- a/client/src/common/types.ts +++ b/client/src/common/types.ts @@ -16,6 +16,8 @@ export function isEphemeralAgent(agentId: string | null | undefined): boolean { export interface ConfigFieldDetail { title: string; description: string; + /** Whether the field holds a secret and should be masked (defaults to masked when omitted). */ + sensitive?: boolean; } export type CodeBarProps = { diff --git a/client/src/components/Auth/LoginForm.tsx b/client/src/components/Auth/LoginForm.tsx index c51c2002e3..3d0a2528d5 100644 --- a/client/src/components/Auth/LoginForm.tsx +++ b/client/src/components/Auth/LoginForm.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect, useContext } from 'react'; import { useForm } from 'react-hook-form'; import { Turnstile } from '@marsidev/react-turnstile'; -import { ThemeContext, Spinner, Button, isDark } from '@librechat/client'; +import { ThemeContext, SecretInput, Spinner, Button, isDark } from '@librechat/client'; import type { TLoginUser, TStartupConfig } from 'librechat-data-provider'; import type { TAuthContext } from '~/common'; import { useResendVerificationEmail, useGetStartupConfig } from '~/data-provider'; @@ -31,6 +31,13 @@ const LoginForm: React.FC = ({ onSubmit, startupConfig, error, const useUsernameLogin = config?.ldap?.username; const validTheme = isDark(theme) ? 'dark' : 'light'; const requireCaptcha = Boolean(startupConfig.turnstile?.siteKey); + const authInputClassName = + 'webkit-dark-styles transition-color peer w-full rounded-2xl border border-border-light bg-surface-primary px-3.5 pb-2.5 pt-3 text-text-primary duration-200 hover:border-border-light focus:border-green-500 focus:outline-none focus-visible:border-green-500'; + const authSecretInputClassName = `${authInputClassName} h-auto pr-12`; + const authLabelClassName = + 'absolute start-3 top-1.5 z-10 origin-[0] -translate-y-4 scale-75 transform bg-surface-primary px-2 text-sm text-text-secondary-alt duration-200 peer-placeholder-shown:top-1/2 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:scale-100 peer-focus:top-1.5 peer-focus:-translate-y-4 peer-focus:scale-75 peer-focus:px-2 peer-focus:text-green-600 dark:peer-focus:text-green-500 rtl:peer-focus:left-auto rtl:peer-focus:translate-x-1/4'; + const authSecretButtonClassName = + 'size-9 rounded-xl text-text-secondary-alt hover:bg-transparent hover:text-text-primary'; useEffect(() => { if (error && error.includes('422') && !showResendLink) { @@ -102,13 +109,10 @@ const LoginForm: React.FC = ({ onSubmit, startupConfig, error, : (value) => validateEmail(value, localize('com_auth_email_pattern')), })} aria-invalid={!!errors.email} - className="webkit-dark-styles transition-color peer w-full rounded-2xl border border-border-light bg-surface-primary px-3.5 pb-2.5 pt-3 text-text-primary duration-200 focus:border-green-500 focus:outline-none" + className={authInputClassName} placeholder=" " /> -