From 9400148175ede887fb1eecb5bd63ec1f11cde87e Mon Sep 17 00:00:00 2001 From: Atef Bellaaj Date: Fri, 5 Dec 2025 17:03:30 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20feat:=20Add=20configurable?= =?UTF-8?q?=20trust=20checkbox=20labels=20for=20MCP=20Server=20Dialog=20(#?= =?UTF-8?q?10820)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Atef Bellaaj --- .../SidePanel/MCPBuilder/MCPServerDialog.tsx | 36 ++++++++++++++++--- client/src/hooks/index.ts | 1 + client/src/hooks/useLocalizedConfig.ts | 36 +++++++++++++++++++ librechat.example.yaml | 12 +++++++ packages/data-provider/src/config.ts | 10 ++++++ 5 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 client/src/hooks/useLocalizedConfig.ts diff --git a/client/src/components/SidePanel/MCPBuilder/MCPServerDialog.tsx b/client/src/components/SidePanel/MCPBuilder/MCPServerDialog.tsx index 4a210dfadc..e978ee7114 100644 --- a/client/src/components/SidePanel/MCPBuilder/MCPServerDialog.tsx +++ b/client/src/components/SidePanel/MCPBuilder/MCPServerDialog.tsx @@ -22,7 +22,8 @@ import { } from '~/data-provider/MCP'; import MCPAuth, { type AuthConfig, AuthTypeEnum, AuthorizationTypeEnum } from './MCPAuth'; import MCPIcon from '~/components/SidePanel/Agents/MCPIcon'; -import { useLocalize } from '~/hooks'; +import { useLocalize, useLocalizedConfig } from '~/hooks'; +import { useGetStartupConfig } from '~/data-provider'; import { cn } from '~/utils'; import { SystemRoles, @@ -69,6 +70,8 @@ export default function MCPServerDialog({ }: MCPServerDialogProps) { const localize = useLocalize(); const { showToast } = useToastContext(); + const { data: startupConfig } = useGetStartupConfig(); + const getLocalizedValue = useLocalizedConfig(); // Mutations const createMutation = useCreateMCPServerMutation(); @@ -575,13 +578,38 @@ export default function MCPServerDialog({ diff --git a/client/src/hooks/index.ts b/client/src/hooks/index.ts index a33d1d0b92..4df82be15a 100644 --- a/client/src/hooks/index.ts +++ b/client/src/hooks/index.ts @@ -34,3 +34,4 @@ export { default as useSpeechToText } from './Input/useSpeechToText'; export { default as useTextToSpeech } from './Input/useTextToSpeech'; export { default as useGenerationsByLatest } from './useGenerationsByLatest'; export { useResourcePermissions } from './useResourcePermissions'; +export { default as useLocalizedConfig } from './useLocalizedConfig'; diff --git a/client/src/hooks/useLocalizedConfig.ts b/client/src/hooks/useLocalizedConfig.ts new file mode 100644 index 0000000000..6cd759be5d --- /dev/null +++ b/client/src/hooks/useLocalizedConfig.ts @@ -0,0 +1,36 @@ +import { useRecoilValue } from 'recoil'; +import store from '~/store'; + +type LocalizedValue = string | Record | undefined; + +/** + * Hook to resolve localized config values based on current user language. + * Automatically retrieves the current language from Recoil state. + * + * @returns A function to resolve localized values + */ +export default function useLocalizedConfig() { + const lang = useRecoilValue(store.lang); + + /** + * Resolves a localized config value. + * @param value - Either a string or object with language codes as keys + * @param fallback - Fallback value if config is undefined or language not found + * @returns The resolved string value + */ + return (value: LocalizedValue, fallback: string): string => { + if (value === undefined) { + return fallback; + } + if (typeof value === 'string') { + return value; + } + // Extract base language code (e.g., 'de' from 'de-DE') + const baseLang = lang?.split('-')[0] ?? 'en'; + + // Try exact locale (de-DE), then base language (de), then 'en', then first available + return ( + (lang && value[lang]) || value[baseLang] || value['en'] || Object.values(value)[0] || fallback + ); + }; +} diff --git a/librechat.example.yaml b/librechat.example.yaml index da0b0b7bb1..2d0cb80abd 100644 --- a/librechat.example.yaml +++ b/librechat.example.yaml @@ -105,6 +105,18 @@ interface: use: false create: false share: false + # Creation / edit MCP server config Dialog config example + # trustCheckbox: + # label: + # en: 'I understand and I want to continue' + # de: 'Ich verstehe und möchte fortfahren' + # de-DE: 'Ich verstehe und möchte fortfahren' # You can narrow translation to regions like (de-DE or de-CH) + # subLabel: + # en: | + # Librechat hasn't reviewed this MCP server. Attackers may attempt to steal your data or trick the model into taking unintended actions, including destroying data. Learn more. + # de: | + # LibreChat hat diesen MCP-Server nicht überprüft. Angreifer könnten versuchen, Ihre Daten zu stehlen oder das Modell zu unbeabsichtigten Aktionen zu verleiten, einschließlich der Zerstörung von Daten. Mehr erfahren. + # Temporary chat retention period in hours (default: 720, min: 1, max: 8760) # temporaryChatRetention: 1 diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts index e4ed1f5ef0..bcbcb58cc2 100644 --- a/packages/data-provider/src/config.ts +++ b/packages/data-provider/src/config.ts @@ -516,12 +516,22 @@ const termsOfServiceSchema = z.object({ export type TTermsOfService = z.infer; +// Schema for localized string (either simple string or language-keyed object) +const localizedStringSchema = z.union([z.string(), z.record(z.string())]); +export type LocalizedString = z.infer; + const mcpServersSchema = z .object({ placeholder: z.string().optional(), use: z.boolean().optional(), create: z.boolean().optional(), share: z.boolean().optional(), + trustCheckbox: z + .object({ + label: localizedStringSchema.optional(), + subLabel: localizedStringSchema.optional(), + }) + .optional(), }) .optional();