⚙️ feat: Add configurable trust checkbox labels for MCP Server Dialog (#10820)

Co-authored-by: Atef Bellaaj <slalom.bellaaj@external.daimlertruck.com>
This commit is contained in:
Atef Bellaaj 2025-12-05 17:03:30 +01:00 committed by Danny Avila
parent 394bb6242b
commit 9400148175
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
5 changed files with 91 additions and 4 deletions

View file

@ -22,7 +22,8 @@ import {
} from '~/data-provider/MCP'; } from '~/data-provider/MCP';
import MCPAuth, { type AuthConfig, AuthTypeEnum, AuthorizationTypeEnum } from './MCPAuth'; import MCPAuth, { type AuthConfig, AuthTypeEnum, AuthorizationTypeEnum } from './MCPAuth';
import MCPIcon from '~/components/SidePanel/Agents/MCPIcon'; 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 { cn } from '~/utils';
import { import {
SystemRoles, SystemRoles,
@ -69,6 +70,8 @@ export default function MCPServerDialog({
}: MCPServerDialogProps) { }: MCPServerDialogProps) {
const localize = useLocalize(); const localize = useLocalize();
const { showToast } = useToastContext(); const { showToast } = useToastContext();
const { data: startupConfig } = useGetStartupConfig();
const getLocalizedValue = useLocalizedConfig();
// Mutations // Mutations
const createMutation = useCreateMCPServerMutation(); const createMutation = useCreateMCPServerMutation();
@ -575,13 +578,38 @@ export default function MCPServerDialog({
<Label <Label
id="trust-this-mcp-label" id="trust-this-mcp-label"
htmlFor="trust" htmlFor="trust"
className="flex cursor-pointer flex-col text-sm font-medium" className="flex cursor-pointer flex-col break-words text-sm font-medium"
> >
<span> <span>
{localize('com_ui_trust_app')} <span className="text-red-500">*</span> {startupConfig?.interface?.mcpServers?.trustCheckbox?.label ? (
<span
/** No sanitization required. trusted admin-controlled source (yml) */
dangerouslySetInnerHTML={{
__html: getLocalizedValue(
startupConfig.interface.mcpServers.trustCheckbox.label,
localize('com_ui_trust_app'),
),
}}
/>
) : (
localize('com_ui_trust_app')
)}{' '}
<span className="text-red-500">*</span>
</span> </span>
<span className="text-xs font-normal text-text-secondary"> <span className="text-xs font-normal text-text-secondary">
{localize('com_agents_mcp_trust_subtext')} {startupConfig?.interface?.mcpServers?.trustCheckbox?.subLabel ? (
<span
/** No sanitization required. trusted admin-controlled source (yml) */
dangerouslySetInnerHTML={{
__html: getLocalizedValue(
startupConfig.interface.mcpServers.trustCheckbox.subLabel,
localize('com_agents_mcp_trust_subtext'),
),
}}
/>
) : (
localize('com_agents_mcp_trust_subtext')
)}
</span> </span>
</Label> </Label>
</div> </div>

View file

@ -34,3 +34,4 @@ export { default as useSpeechToText } from './Input/useSpeechToText';
export { default as useTextToSpeech } from './Input/useTextToSpeech'; export { default as useTextToSpeech } from './Input/useTextToSpeech';
export { default as useGenerationsByLatest } from './useGenerationsByLatest'; export { default as useGenerationsByLatest } from './useGenerationsByLatest';
export { useResourcePermissions } from './useResourcePermissions'; export { useResourcePermissions } from './useResourcePermissions';
export { default as useLocalizedConfig } from './useLocalizedConfig';

View file

@ -0,0 +1,36 @@
import { useRecoilValue } from 'recoil';
import store from '~/store';
type LocalizedValue = string | Record<string, string> | 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
);
};
}

View file

@ -105,6 +105,18 @@ interface:
use: false use: false
create: false create: false
share: 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. <a href="https://google.de" target="_blank"><strong>Learn more.</strong></a>
# 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. <a href="https://google.de" target="_blank"><strong>Mehr erfahren.</strong></a>
# Temporary chat retention period in hours (default: 720, min: 1, max: 8760) # Temporary chat retention period in hours (default: 720, min: 1, max: 8760)
# temporaryChatRetention: 1 # temporaryChatRetention: 1

View file

@ -516,12 +516,22 @@ const termsOfServiceSchema = z.object({
export type TTermsOfService = z.infer<typeof termsOfServiceSchema>; export type TTermsOfService = z.infer<typeof termsOfServiceSchema>;
// 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<typeof localizedStringSchema>;
const mcpServersSchema = z const mcpServersSchema = z
.object({ .object({
placeholder: z.string().optional(), placeholder: z.string().optional(),
use: z.boolean().optional(), use: z.boolean().optional(),
create: z.boolean().optional(), create: z.boolean().optional(),
share: z.boolean().optional(), share: z.boolean().optional(),
trustCheckbox: z
.object({
label: localizedStringSchema.optional(),
subLabel: localizedStringSchema.optional(),
})
.optional(),
}) })
.optional(); .optional();