diff --git a/ui/litellm-dashboard/src/components/mcp_tools/OAuthFormFields.tsx b/ui/litellm-dashboard/src/components/mcp_tools/OAuthFormFields.tsx index 27d90d5ae1a..99ab1c4edcf 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/OAuthFormFields.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/OAuthFormFields.tsx @@ -2,6 +2,7 @@ import React from "react"; import { Form, Input, InputNumber, Select, Tooltip } from "antd"; import { InfoCircleOutlined } from "@ant-design/icons"; import { Button, TextInput } from "@tremor/react"; +import { useTranslation } from "react-i18next"; import { OAUTH_FLOW } from "./types"; interface OAuthFlowStatus { @@ -38,15 +39,16 @@ const OAuthFormFields: React.FC = ({ initialFlowType, docsUrl, }) => { - const placeholderSuffix = isEditing ? " (leave blank to keep existing)" : ""; + const { t } = useTranslation(); + const placeholderSuffix = isEditing ? ` (${t("mcpTools.oAuthFormFields.leaveBlankToKeep")})` : ""; return ( <> } name="oauth_flow_type" @@ -55,14 +57,14 @@ const OAuthFormFields: React.FC = ({ @@ -71,46 +73,65 @@ const OAuthFormFields: React.FC = ({ {isM2M ? ( <> } + label={ + + } name={["credentials", "client_id"]} - rules={[{ required: true, message: "Client ID is required for M2M OAuth" }]} + rules={[{ required: true, message: t("mcpTools.oAuthFormFields.clientIdM2MRequired") }]} > + } name={["credentials", "client_secret"]} - rules={[{ required: true, message: "Client Secret is required for M2M OAuth" }]} + rules={[{ required: true, message: t("mcpTools.oAuthFormFields.clientSecretM2MRequired") }]} > } + label={ + + } name="token_url" - rules={[{ required: true, message: "Token URL is required for M2M OAuth" }]} + rules={[{ required: true, message: t("mcpTools.oAuthFormFields.tokenUrlM2MRequired") }]} > } name={["credentials", "scopes"]} > - ) : ( @@ -119,8 +140,8 @@ const OAuthFormFields: React.FC = ({ label={ {docsUrl && ( = ({ className="text-xs text-blue-500 hover:text-blue-700 ml-2 font-normal" onClick={(e) => e.stopPropagation()} > - Create OAuth App → + {t("mcpTools.oAuthFormFields.createOAuthAppLink")} )} } name={["credentials", "client_id"]} - > - - - - } - name={["credentials", "client_secret"]} > } - name={["credentials", "scopes"]} + name={["credentials", "client_secret"]} > - + + } name="authorization_url" @@ -177,7 +208,12 @@ const OAuthFormFields: React.FC = ({ } + label={ + + } name="token_url" > @@ -185,8 +221,8 @@ const OAuthFormFields: React.FC = ({ } name="registration_url" @@ -196,8 +232,8 @@ const OAuthFormFields: React.FC = ({ } name="token_validation_json" @@ -209,7 +245,7 @@ const OAuthFormFields: React.FC = ({ JSON.parse(value); return Promise.resolve(); } catch { - return Promise.reject(new Error("Must be valid JSON")); + return Promise.reject(new Error(t("mcpTools.oAuthFormFields.mustBeValidJson"))); } }, }, @@ -224,35 +260,37 @@ const OAuthFormFields: React.FC = ({ } name="token_storage_ttl_seconds" > - + {oauthFlow && (
-

- Use OAuth to fetch a fresh access token and temporarily save it in the session as the authentication - value. -

+

{t("mcpTools.oAuthFormFields.oauthFlowDescription")}

{oauthFlow.error &&

{oauthFlow.error}

} {oauthFlow.status === "success" && oauthFlow.tokenResponse?.access_token && (

- Token fetched. Expires in {oauthFlow.tokenResponse.expires_in ?? "?"} seconds. + {t("mcpTools.oAuthFormFields.tokenFetched", { seconds: oauthFlow.tokenResponse.expires_in ?? "?" })}

)}
diff --git a/ui/litellm-dashboard/src/components/mcp_tools/OpenAPIFormSection.tsx b/ui/litellm-dashboard/src/components/mcp_tools/OpenAPIFormSection.tsx index d606b8ba1fc..ce7f8a62f81 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/OpenAPIFormSection.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/OpenAPIFormSection.tsx @@ -2,6 +2,7 @@ import React, { useState } from "react"; import { Form, Input, Tooltip } from "antd"; import { InfoCircleOutlined } from "@ant-design/icons"; import { FormInstance } from "antd/es/form"; +import { useTranslation } from "react-i18next"; import { AUTH_TYPE, OAUTH_FLOW } from "./types"; import OpenAPIQuickPicker, { OpenAPIRegistryEntry, OpenAPIKeyTool } from "./OpenAPIQuickPicker"; @@ -31,6 +32,7 @@ const OpenAPIFormSection: React.FC = ({ onLogoUrlChange, onOAuthDocsUrlChange, }) => { + const { t } = useTranslation(); const [selectedPreset, setSelectedPreset] = useState(null); const handlePresetSelect = (entry: OpenAPIRegistryEntry) => { @@ -66,14 +68,14 @@ const OpenAPIFormSection: React.FC = ({ - OpenAPI Spec URL - + {t("mcpTools.openAPIFormSection.specUrlLabel")} + } name="spec_path" - rules={[{ required: true, message: "Please enter an OpenAPI spec URL" }]} + rules={[{ required: true, message: t("mcpTools.openAPIFormSection.specUrlRequired") }]} > = ({ accessToken, selectedName, onSelect }) => { + const { t } = useTranslation(); const [apis, setApis] = useState([]); const [loading, setLoading] = useState(false); const [imgErrors, setImgErrors] = useState>(new Set()); @@ -49,7 +51,7 @@ const OpenAPIQuickPicker: React.FC = ({ accessToken, se if (loading) { return (
- Popular APIs + {t("mcpTools.openAPIQuickPicker.popularApis")}
@@ -61,7 +63,9 @@ const OpenAPIQuickPicker: React.FC = ({ accessToken, se return (
- Popular APIs + + {t("mcpTools.openAPIQuickPicker.popularApis")} +
{apis.map((api) => { @@ -98,9 +102,7 @@ const OpenAPIQuickPicker: React.FC = ({ accessToken, se })}
-

- Select an API to pre-fill the spec URL and OAuth 2.0 settings, or enter your own spec URL below. -

+

{t("mcpTools.openAPIQuickPicker.selectHint")}

); }; diff --git a/ui/litellm-dashboard/src/components/mcp_tools/StdioConfiguration.tsx b/ui/litellm-dashboard/src/components/mcp_tools/StdioConfiguration.tsx index 476a5b61683..1c8495b9d93 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/StdioConfiguration.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/StdioConfiguration.tsx @@ -1,6 +1,7 @@ import React from "react"; import { Form, Input, Tooltip } from "antd"; import { InfoCircleOutlined } from "@ant-design/icons"; +import { useTranslation } from "react-i18next"; interface StdioConfigurationProps { isVisible: boolean; @@ -12,21 +13,23 @@ interface StdioConfigurationProps { } const StdioConfiguration: React.FC = ({ isVisible, required = true }) => { + const { t } = useTranslation(); + if (!isVisible) return null; return ( - Stdio Configuration (JSON) - + {t("mcpTools.stdioConfiguration.label")} + } name="stdio_config" rules={[ - ...(required ? [{ required: true, message: "Please enter stdio configuration" }] : []), + ...(required ? [{ required: true, message: t("mcpTools.stdioConfiguration.required") }] : []), { validator: (_, value) => { if (!value) return Promise.resolve(); @@ -34,7 +37,7 @@ const StdioConfiguration: React.FC = ({ isVisible, requ JSON.parse(value); return Promise.resolve(); } catch { - return Promise.reject("Please enter valid JSON"); + return Promise.reject(t("mcpTools.stdioConfiguration.invalidJson")); } }, }, diff --git a/ui/litellm-dashboard/src/components/mcp_tools/ToolTestPanel.tsx b/ui/litellm-dashboard/src/components/mcp_tools/ToolTestPanel.tsx index e3e84cb7434..0f39d49ce6a 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/ToolTestPanel.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/ToolTestPanel.tsx @@ -3,6 +3,7 @@ import { Button, TextInput } from "@tremor/react"; import { MCPTool, InputSchema, InputSchemaProperty } from "./types"; import { Form, Select, Tooltip } from "antd"; import { InfoCircleOutlined } from "@ant-design/icons"; +import { useTranslation } from "react-i18next"; import NotificationsManager from "../molecules/notifications_manager"; const isPlainObject = (value: unknown): value is Record => @@ -113,6 +114,7 @@ export function ToolTestPanel({ error: Error | null; onClose: () => void; }) { + const { t } = useTranslation(); const [form] = Form.useForm(); const [viewMode, setViewMode] = React.useState<"formatted" | "json">("formatted"); const [startTime, setStartTime] = React.useState(null); @@ -278,18 +280,18 @@ export function ToolTestPanel({ const handleCopyResult = async () => { const success = await copyToClipboard(JSON.stringify(result, null, 2)); if (success) { - NotificationsManager.success("Result copied to clipboard"); + NotificationsManager.success(t("mcpTools.toolTestPanel.resultCopied")); } else { - NotificationsManager.fromBackend("Failed to copy result"); + NotificationsManager.fromBackend(t("mcpTools.toolTestPanel.copyResultFailed")); } }; const handleCopyToolName = async () => { const success = await copyToClipboard(tool.name); if (success) { - NotificationsManager.success("Tool name copied to clipboard"); + NotificationsManager.success(t("mcpTools.toolTestPanel.toolNameCopied")); } else { - NotificationsManager.fromBackend("Failed to copy tool name"); + NotificationsManager.fromBackend(t("mcpTools.toolTestPanel.copyToolNameFailed")); } }; @@ -308,11 +310,11 @@ export function ToolTestPanel({ )}
-

Test Tool:

+

{t("mcpTools.toolTestPanel.testToolTitle")}

{tool.name}

{tool.description}

-

Provider: {tool.mcp_info.server_name}

+

+ {t("mcpTools.toolTestPanel.provider", { name: tool.mcp_info.server_name })} +

@@ -538,7 +552,7 @@ export function ToolTestPanel({ {/* Right Column - Tool Result */}
-

Tool Result

+

{t("mcpTools.toolTestPanel.toolResult")}

@@ -561,10 +575,10 @@ export function ToolTestPanel({ />
-

Ready to Call Tool

-

- Configure the input parameters and click "Call Tool" to see the results here. -

+

+ {t("mcpTools.toolTestPanel.readyToCallTitle")} +

+

{t("mcpTools.toolTestPanel.readyToCallDesc")}

) : ( @@ -582,7 +596,9 @@ export function ToolTestPanel({ d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /> -

Tool executed successfully

+

+ {t("mcpTools.toolTestPanel.executedSuccessfully")} +

{duration !== null && ( • {(duration / 1000).toFixed(2)}s )} @@ -598,7 +614,7 @@ export function ToolTestPanel({ : "text-green-600 hover:text-green-800" }`} > - Formatted + {t("mcpTools.toolTestPanel.viewFormatted")}