From cd8fbb570902161f2f2687a56277407f6a5fd75a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B9=E9=9B=AA?= <2b@yorha.xyz> Date: Wed, 10 Jun 2026 21:20:59 +0900 Subject: [PATCH] feat(ui): i18n for MCP tool configuration and tools list --- .../mcp_tools/mcp_tool_configuration.tsx | 212 ++++++++++-------- .../src/components/mcp_tools/mcp_tools.tsx | 69 +++--- ui/litellm-dashboard/src/locales/en.json | 59 +++++ ui/litellm-dashboard/src/locales/zh-CN.json | 59 +++++ 4 files changed, 271 insertions(+), 128 deletions(-) diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_tool_configuration.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_tool_configuration.tsx index 870cdfb1e3b..9b9ebb603c5 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_tool_configuration.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_tool_configuration.tsx @@ -1,4 +1,5 @@ import React, { useEffect, useMemo, useRef, useState } from "react"; +import { useTranslation, Trans } from "react-i18next"; import { Card, Title, Text } from "@tremor/react"; import { ToolOutlined, CheckCircleOutlined, SearchOutlined, EditOutlined } from "@ant-design/icons"; import { Badge, Spin, Checkbox, Input, Radio } from "antd"; @@ -59,86 +60,93 @@ const ToolRow: React.FC = ({ onToggleExpand, onDisplayNameChange, onDescriptionChange, -}) => ( -
-
onToggle(tool.name)}> -
- onToggle(tool.name)} /> -
-
- {toolNameToDisplayName[tool.name] || tool.name} - - {isEnabled ? "Enabled" : "Disabled"} - - {toolNameToDisplayName[tool.name] && ( - - Custom name +}) => { + const { t } = useTranslation(); + return ( +
+
onToggle(tool.name)}> +
+ onToggle(tool.name)} /> +
+
+ {toolNameToDisplayName[tool.name] || tool.name} + + {isEnabled ? t("common.enabled") : t("common.disabled")} + {toolNameToDisplayName[tool.name] && ( + + {t("mcpTools.mcpToolConfiguration.customName")} + + )} +
+ {(toolNameToDescription[tool.name] || tool.description) && ( + + {toolNameToDescription[tool.name] || tool.description} + )} -
- {(toolNameToDescription[tool.name] || tool.description) && ( - - {toolNameToDescription[tool.name] || tool.description} + + {isEnabled + ? t("mcpTools.mcpToolConfiguration.toolCallableByUsers") + : t("mcpTools.mcpToolConfiguration.toolBlockedFromUsers")} - )} - - {isEnabled ? "✓ Users can call this tool" : "✗ Users cannot call this tool"} - +
+
-
+ {isEditExpanded && ( +
e.stopPropagation()} > - - -
+
+ + {t("mcpTools.mcpToolConfiguration.displayName")} + + onDisplayNameChange(tool.name, e.target.value)} + /> + + {t("mcpTools.mcpToolConfiguration.displayNameHelp")} + +
+
+ {t("common.description")} + onDescriptionChange(tool.name, e.target.value)} + rows={2} + /> + + {t("mcpTools.mcpToolConfiguration.descriptionHelp")} + +
+
+ )}
- {isEditExpanded && ( -
e.stopPropagation()} - > -
- Display Name - onDisplayNameChange(tool.name, e.target.value)} - /> - - Override how this tool's name appears to users. Leave blank to use original. - -
-
- Description - onDescriptionChange(tool.name, e.target.value)} - rows={2} - /> - - Override the tool description shown to users. Leave blank to use original. - -
-
- )} -
-); + ); +}; const MCPToolConfiguration: React.FC = ({ accessToken, @@ -159,6 +167,7 @@ const MCPToolConfiguration: React.FC = ({ externalCanFetch, isEditMode = false, }) => { + const { t } = useTranslation(); const previousToolsRef = useRef([]); const [toolSearchTerm, setToolSearchTerm] = useState(""); const [viewMode, setViewMode] = useState<"crud" | "flat">("crud"); @@ -387,7 +396,7 @@ const MCPToolConfiguration: React.FC = ({
- Tool Configuration + {t("mcpTools.mcpToolConfiguration.title")} {tools.length > 0 && ( = ({ optionType="button" buttonStyle="solid" options={[ - { label: "Risk Groups", value: "crud" }, - { label: "Flat List", value: "flat" }, + { label: t("mcpTools.mcpToolConfiguration.viewRiskGroups"), value: "crud" }, + { label: t("mcpTools.mcpToolConfiguration.viewFlatList"), value: "flat" }, ]} /> )} @@ -415,8 +424,7 @@ const MCPToolConfiguration: React.FC = ({ {/* Description */}
- Select which tools users can call: Only checked tools will be available for users to - invoke. Unchecked tools will be blocked from execution. + }} />
@@ -424,7 +432,7 @@ const MCPToolConfiguration: React.FC = ({ {isLoadingTools && (
- Loading tools from spec... + {t("mcpTools.mcpToolConfiguration.loadingFromSpec")}
)} @@ -432,7 +440,7 @@ const MCPToolConfiguration: React.FC = ({ {toolsError && !isLoadingTools && (
- Unable to load tools + {t("mcpTools.mcpToolConfiguration.unableToLoadTools")}
{toolsError}
@@ -446,15 +454,19 @@ const MCPToolConfiguration: React.FC = ({ (keyTools && keyTools.length > 0 ? (
- No tools loaded from spec - Expected tools: {keyTools.map((t) => t.name).join(", ")} + {t("mcpTools.mcpToolConfiguration.noToolsLoadedFromSpec")} + + {t("mcpTools.mcpToolConfiguration.expectedTools", { + tools: keyTools.map((tool) => tool.name).join(", "), + })} +
) : (
- No tools available for configuration + {t("mcpTools.mcpToolConfiguration.noToolsAvailable")}
- Connect to an MCP server with tools to configure them + {t("mcpTools.mcpToolConfiguration.connectMcpServerHint")}
))} @@ -462,9 +474,9 @@ const MCPToolConfiguration: React.FC = ({ {!canFetchTools && (formValues.url || formValues.spec_path) && (
- Complete required fields to configure tools + {t("mcpTools.mcpToolConfiguration.completeRequiredFields")}
- Fill in URL, Transport, and Authentication to load available tools + {t("mcpTools.mcpToolConfiguration.fillInFieldsHint")}
)} @@ -474,14 +486,16 @@ const MCPToolConfiguration: React.FC = ({
- {effectiveAllowedTools.length} of {tools.length} {tools.length === 1 ? "tool" : "tools"} enabled for - user access + {t("mcpTools.mcpToolConfiguration.toolsEnabled", { + enabled: effectiveAllowedTools.length, + count: tools.length, + })}
{/* Search box shared by both views */} } value={toolSearchTerm} onChange={(e) => setToolSearchTerm(e.target.value)} @@ -506,28 +520,30 @@ const MCPToolConfiguration: React.FC = ({ {filteredTools.length === 0 ? (
- No tools found matching "{toolSearchTerm}" + {t("mcpTools.mcpToolConfiguration.noToolsMatchSearch", { term: toolSearchTerm })}
) : (
{pinnedFiltered.length > 0 && ( <>
-

Suggested tools

+

+ {t("mcpTools.mcpToolConfiguration.suggestedTools")} +

@@ -551,7 +567,9 @@ const MCPToolConfiguration: React.FC = ({ <>

- {pinnedFiltered.length > 0 ? "All tools" : "Tools"} + {pinnedFiltered.length > 0 + ? t("mcpTools.mcpToolConfiguration.allTools") + : t("mcpTools.mcpToolConfiguration.tools")}

diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.tsx index d94dfbb966f..b7fc9bd412a 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.tsx @@ -1,4 +1,5 @@ import React, { useCallback, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; import { useQuery, useMutation } from "@tanstack/react-query"; import { ToolTestPanel } from "./ToolTestPanel"; import { MCPTool, MCPToolsViewerProps, MCPContent, CallMCPToolResponse, getMcpOAuthMode } from "./types"; @@ -25,6 +26,7 @@ const MCPToolsViewer = ({ serverAlias, extraHeaders, }: MCPToolsViewerProps) => { + const { t } = useTranslation(); const [selectedTool, setSelectedTool] = useState(null); const [toolResult, setToolResult] = useState(null); const [toolError, setToolError] = useState(null); @@ -260,7 +262,7 @@ const MCPToolsViewer = ({
{/* Left Sidebar with Controls */}
- MCP Tools + {t("mcpTools.mcpTools.title")}
{/* Extra Headers Input Section */} @@ -269,7 +271,9 @@ const MCPToolsViewer = ({
- Additional Headers + + {t("mcpTools.mcpTools.additionalHeaders")} +
setShowHeaderInput(!showHeaderInput)} className="text-blue-700 p-0 h-auto" > - {showHeaderInput ? "Hide" : "Configure"} + {showHeaderInput ? t("mcpTools.mcpTools.hide") : t("mcpTools.mcpTools.configure")}
{!showHeaderInput && Object.keys(passthroughHeaders).length === 0 && ( - - This server requires additional headers. Click "Configure" to provide values. - + {t("mcpTools.mcpTools.headersRequiredHint")} )} {showHeaderInput && ( @@ -294,7 +296,7 @@ const MCPToolsViewer = ({ { setPassthroughHeaders({ @@ -317,7 +319,7 @@ const MCPToolsViewer = ({ disabled={Object.values(passthroughHeaders).every((v) => !v || !v.trim())} className="w-full mt-2" > - Load Tools + {t("mcpTools.mcpTools.loadTools")}
)} @@ -326,7 +328,7 @@ const MCPToolsViewer = ({
- {Object.keys(passthroughHeaders).length} header(s) configured + {t("mcpTools.mcpTools.headersConfigured", { count: Object.keys(passthroughHeaders).length })}
)} @@ -336,7 +338,7 @@ const MCPToolsViewer = ({ {/* Tool Selection - Show tools first */}
- Available Tools + {t("mcpTools.mcpTools.availableTools")} {toolsData.length > 0 && ( {toolsData.length} @@ -348,8 +350,8 @@ const MCPToolsViewer = ({ {isPassthrough && !oauthToken && (
-

Authentication required

-

Authenticate to view available tools

+

{t("mcpTools.mcpTools.authRequired")}

+

{t("mcpTools.mcpTools.authenticateToViewTools")}

- Authorize + {t("mcpTools.mcpTools.authorize")} {oauthError &&

{oauthError}

}
@@ -369,10 +371,8 @@ const MCPToolsViewer = ({ {oboNeedsAuth && (
-

Authentication required

-

- Authenticate with the upstream provider to view available tools -

+

{t("mcpTools.mcpTools.authRequired")}

+

{t("mcpTools.mcpTools.authenticateWithUpstream")}

- Authorize + {t("mcpTools.mcpTools.authorize")} {dbOAuthError &&

{dbOAuthError}

}
@@ -392,7 +392,7 @@ const MCPToolsViewer = ({ {toolsData.length > 0 && (
} value={toolSearchTerm} onChange={(e) => setToolSearchTerm(e.target.value)} @@ -410,7 +410,7 @@ const MCPToolsViewer = ({
-

Loading tools...

+

{t("mcpTools.mcpTools.loadingTools")}

)} @@ -418,7 +418,8 @@ const MCPToolsViewer = ({ {(mcpToolsResponse?.error || mcpToolsError) && !toolsAreaLoading && !toolsData.length && (

- Error: {mcpToolsResponse?.message || (mcpToolsError as Error)?.message} + {t("mcpTools.mcpTools.errorPrefix")} + {mcpToolsResponse?.message || (mcpToolsError as Error)?.message}

)} @@ -444,8 +445,10 @@ const MCPToolsViewer = ({ />
-

No tools available

-

No tools found for this server

+

+ {t("mcpTools.mcpTools.noToolsAvailable")} +

+

{t("mcpTools.mcpTools.noToolsFoundForServer")}

)} @@ -455,8 +458,12 @@ const MCPToolsViewer = ({ {filteredTools.length === 0 ? (
-

No tools found

-

No tools match "{toolSearchTerm}"

+

+ {t("mcpTools.mcpTools.noToolsFound")} +

+

+ {t("mcpTools.mcpTools.noToolsMatchSearch", { term: toolSearchTerm })} +

) : (
)} @@ -509,7 +516,7 @@ const MCPToolsViewer = ({ clipRule="evenodd" /> - Selected + {t("mcpTools.mcpTools.selected")}
)} @@ -528,7 +535,7 @@ const MCPToolsViewer = ({ {/* Main Testing Area */}
- Tool Testing Playground + {t("mcpTools.mcpTools.testingPlaygroundTitle")}
@@ -536,10 +543,10 @@ const MCPToolsViewer = ({ /* Empty State */
- Select a Tool to Test - - Choose a tool from the left sidebar to start testing its functionality with custom inputs. + + {t("mcpTools.mcpTools.selectToolToTest")} + {t("mcpTools.mcpTools.selectToolHint")}
) : ( /* Tool Test Panel */ diff --git a/ui/litellm-dashboard/src/locales/en.json b/ui/litellm-dashboard/src/locales/en.json index f5c38c105fe..7540f5c94e9 100644 --- a/ui/litellm-dashboard/src/locales/en.json +++ b/ui/litellm-dashboard/src/locales/en.json @@ -3045,6 +3045,65 @@ } }, "mcpTools": { + "mcpToolConfiguration": { + "title": "Tool Configuration", + "viewRiskGroups": "Risk Groups", + "viewFlatList": "Flat List", + "selectToolsDescription": "Select which tools users can call: Only checked tools will be available for users to invoke. Unchecked tools will be blocked from execution.", + "loadingFromSpec": "Loading tools from spec...", + "unableToLoadTools": "Unable to load tools", + "noToolsLoadedFromSpec": "No tools loaded from spec", + "expectedTools": "Expected tools: {{tools}}", + "noToolsAvailable": "No tools available for configuration", + "connectMcpServerHint": "Connect to an MCP server with tools to configure them", + "completeRequiredFields": "Complete required fields to configure tools", + "fillInFieldsHint": "Fill in URL, Transport, and Authentication to load available tools", + "toolsEnabled_one": "{{enabled}} of {{count}} tool enabled for user access", + "toolsEnabled_other": "{{enabled}} of {{count}} tools enabled for user access", + "searchPlaceholder": "Search tools by name or description...", + "noToolsMatchSearch": "No tools found matching \"{{term}}\"", + "suggestedTools": "Suggested tools", + "enableAll": "Enable all", + "disableAll": "Disable all", + "allTools": "All tools", + "tools": "Tools", + "customName": "Custom name", + "toolCallableByUsers": "✓ Users can call this tool", + "toolBlockedFromUsers": "✗ Users cannot call this tool", + "editDisplayNameTitle": "Edit display name and description", + "displayName": "Display Name", + "displayNameHelp": "Override how this tool's name appears to users. Leave blank to use original.", + "noDescription": "No description", + "descriptionHelp": "Override the tool description shown to users. Leave blank to use original." + }, + "mcpTools": { + "title": "MCP Tools", + "additionalHeaders": "Additional Headers", + "hide": "Hide", + "configure": "Configure", + "headersRequiredHint": "This server requires additional headers. Click \"Configure\" to provide values.", + "enterHeaderPlaceholder": "Enter {{header}}", + "loadTools": "Load Tools", + "headersConfigured_one": "{{count}} header configured", + "headersConfigured_other": "{{count}} header(s) configured", + "availableTools": "Available Tools", + "authRequired": "Authentication required", + "authenticateToViewTools": "Authenticate to view available tools", + "authenticateWithUpstream": "Authenticate with the upstream provider to view available tools", + "authorize": "Authorize", + "searchToolsPlaceholder": "Search tools...", + "loadingTools": "Loading tools...", + "errorPrefix": "Error: ", + "noToolsAvailable": "No tools available", + "noToolsFoundForServer": "No tools found for this server", + "noToolsFound": "No tools found", + "noToolsMatchSearch": "No tools match \"{{term}}\"", + "serverLogoAlt": "{{name}} logo", + "selected": "Selected", + "testingPlaygroundTitle": "Tool Testing Playground", + "selectToolToTest": "Select a Tool to Test", + "selectToolHint": "Choose a tool from the left sidebar to start testing its functionality with custom inputs." + }, "byokCredentialModal": { "pleaseEnterApiKey": "Please enter your API key", "connectedSuccess": "Connected to {{name}}", diff --git a/ui/litellm-dashboard/src/locales/zh-CN.json b/ui/litellm-dashboard/src/locales/zh-CN.json index 87916789452..fa60c145067 100644 --- a/ui/litellm-dashboard/src/locales/zh-CN.json +++ b/ui/litellm-dashboard/src/locales/zh-CN.json @@ -3045,6 +3045,65 @@ } }, "mcpTools": { + "mcpToolConfiguration": { + "title": "工具配置", + "viewRiskGroups": "风险分组", + "viewFlatList": "平铺列表", + "selectToolsDescription": "选择用户可调用的工具:只有勾选的工具才对用户开放调用,未勾选的工具将被阻止执行。", + "loadingFromSpec": "正在从 spec 加载工具...", + "unableToLoadTools": "无法加载工具", + "noToolsLoadedFromSpec": "未从 spec 加载到工具", + "expectedTools": "预期工具:{{tools}}", + "noToolsAvailable": "暂无可配置的工具", + "connectMcpServerHint": "连接到带有工具的 MCP 服务器以进行配置", + "completeRequiredFields": "请填写必填字段以配置工具", + "fillInFieldsHint": "填写 URL、传输方式和认证信息以加载可用工具", + "toolsEnabled_one": "已为用户开放 {{enabled}} / {{count}} 个工具", + "toolsEnabled_other": "已为用户开放 {{enabled}} / {{count}} 个工具", + "searchPlaceholder": "按名称或描述搜索工具...", + "noToolsMatchSearch": "未找到匹配「{{term}}」的工具", + "suggestedTools": "推荐工具", + "enableAll": "全部启用", + "disableAll": "全部禁用", + "allTools": "所有工具", + "tools": "工具", + "customName": "自定义名称", + "toolCallableByUsers": "✓ 用户可以调用此工具", + "toolBlockedFromUsers": "✗ 用户无法调用此工具", + "editDisplayNameTitle": "编辑显示名称和描述", + "displayName": "显示名称", + "displayNameHelp": "覆盖此工具对用户显示的名称,留空则使用原名称。", + "noDescription": "无描述", + "descriptionHelp": "覆盖向用户显示的工具描述,留空则使用原描述。" + }, + "mcpTools": { + "title": "MCP 工具", + "additionalHeaders": "附加请求头", + "hide": "收起", + "configure": "配置", + "headersRequiredHint": "此服务器需要附加请求头,请点击「配置」提供相应值。", + "enterHeaderPlaceholder": "输入 {{header}}", + "loadTools": "加载工具", + "headersConfigured_one": "已配置 {{count}} 个请求头", + "headersConfigured_other": "已配置 {{count}} 个请求头", + "availableTools": "可用工具", + "authRequired": "需要认证", + "authenticateToViewTools": "请先认证以查看可用工具", + "authenticateWithUpstream": "请向上游提供商认证以查看可用工具", + "authorize": "授权", + "searchToolsPlaceholder": "搜索工具...", + "loadingTools": "加载工具中...", + "errorPrefix": "错误:", + "noToolsAvailable": "暂无可用工具", + "noToolsFoundForServer": "此服务器暂无工具", + "noToolsFound": "未找到工具", + "noToolsMatchSearch": "没有工具匹配「{{term}}」", + "serverLogoAlt": "{{name}} 图标", + "selected": "已选择", + "testingPlaygroundTitle": "工具测试 Playground", + "selectToolToTest": "选择工具进行测试", + "selectToolHint": "从左侧边栏选择工具,开始使用自定义输入测试其功能。" + }, "byokCredentialModal": { "pleaseEnterApiKey": "请输入您的 API 密钥", "connectedSuccess": "已连接到 {{name}}",