feat(ui): i18n for MCP tool permissions

This commit is contained in:
吹雪 2026-06-11 07:33:57 +09:00
parent b69ca5f027
commit 4b29c648ec
3 changed files with 51 additions and 13 deletions

View file

@ -1,4 +1,5 @@
import React, { useEffect, useRef, useState, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { listMCPTools } from "../networking";
import { MCPTool, MCPServer } from "../mcp_tools/types";
import { Text } from "@tremor/react";
@ -22,6 +23,7 @@ const MCPToolPermissions: React.FC<MCPToolPermissionsProps> = ({
onChange,
disabled = false,
}) => {
const { t } = useTranslation();
const { data: allServers = [] } = useMCPServers();
const [serverTools, setServerTools] = useState<Record<string, MCPTool[]>>({});
const [loadingTools, setLoadingTools] = useState<Record<string, boolean>>({});
@ -52,7 +54,10 @@ const MCPToolPermissions: React.FC<MCPToolPermissionsProps> = ({
const response = await listMCPTools(token, serverId);
if (response.error) {
setToolErrors((prev) => ({ ...prev, [serverId]: response.message || "Failed to fetch tools" }));
setToolErrors((prev) => ({
...prev,
[serverId]: response.message || t("mcpServerManagement.mcpToolPermissions.failedToFetchTools"),
}));
setServerTools((prev) => ({ ...prev, [serverId]: [] }));
} else {
const fetchedTools: MCPTool[] = response.tools || [];
@ -63,14 +68,17 @@ const MCPToolPermissions: React.FC<MCPToolPermissionsProps> = ({
const latestPermissions = toolPermissionsRef.current;
if (!latestPermissions[serverId] && fetchedTools.length > 0) {
const nonDeleteTools = fetchedTools
.filter((t) => classifyToolOp(t.name, t.description || "") !== "delete")
.map((t) => t.name);
.filter((tool) => classifyToolOp(tool.name, tool.description || "") !== "delete")
.map((tool) => tool.name);
onChange({ ...latestPermissions, [serverId]: nonDeleteTools });
}
}
} catch (err) {
console.error(`Error fetching tools for server ${serverId}:`, err);
setToolErrors((prev) => ({ ...prev, [serverId]: "Failed to fetch tools" }));
setToolErrors((prev) => ({
...prev,
[serverId]: t("mcpServerManagement.mcpToolPermissions.failedToFetchTools"),
}));
setServerTools((prev) => ({ ...prev, [serverId]: [] }));
} finally {
setLoadingTools((prev) => ({ ...prev, [serverId]: false }));
@ -95,7 +103,7 @@ const MCPToolPermissions: React.FC<MCPToolPermissionsProps> = ({
const handleSelectAll = (serverId: string) => {
const tools = serverTools[serverId] || [];
onChange({ ...toolPermissions, [serverId]: tools.map((t) => t.name) });
onChange({ ...toolPermissions, [serverId]: tools.map((tool) => tool.name) });
};
const handleDeselectAll = (serverId: string) => {
@ -133,8 +141,8 @@ const MCPToolPermissions: React.FC<MCPToolPermissionsProps> = ({
optionType="button"
buttonStyle="solid"
options={[
{ label: "Risk Groups", value: "crud" },
{ label: "Flat List", value: "flat" },
{ label: t("mcpServerManagement.mcpToolPermissions.riskGroups"), value: "crud" },
{ label: t("mcpServerManagement.mcpToolPermissions.flatList"), value: "flat" },
]}
/>
)}
@ -146,7 +154,7 @@ const MCPToolPermissions: React.FC<MCPToolPermissionsProps> = ({
onClick={() => handleSelectAll(server.server_id)}
disabled={isLoading}
>
Select All
{t("mcpServerManagement.mcpToolPermissions.selectAll")}
</button>
<button
type="button"
@ -154,7 +162,7 @@ const MCPToolPermissions: React.FC<MCPToolPermissionsProps> = ({
onClick={() => handleDeselectAll(server.server_id)}
disabled={isLoading}
>
Deselect All
{t("mcpServerManagement.mcpToolPermissions.deselectAll")}
</button>
</>
)}
@ -167,14 +175,16 @@ const MCPToolPermissions: React.FC<MCPToolPermissionsProps> = ({
{isLoading && (
<div className="flex items-center justify-center py-8">
<Spin size="large" />
<Text className="ml-3 text-gray-500">Loading tools...</Text>
<Text className="ml-3 text-gray-500">{t("mcpServerManagement.mcpToolPermissions.loadingTools")}</Text>
</div>
)}
{/* Error */}
{error && !isLoading && (
<div className="p-4 bg-red-50 border border-red-200 rounded-lg text-center">
<Text className="text-red-600 font-medium">Unable to load tools</Text>
<Text className="text-red-600 font-medium">
{t("mcpServerManagement.mcpToolPermissions.unableToLoadTools")}
</Text>
<Text className="text-sm text-red-500 mt-1">{error}</Text>
</div>
)}
@ -212,7 +222,9 @@ const MCPToolPermissions: React.FC<MCPToolPermissionsProps> = ({
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<Text className="font-medium text-gray-900">{tool.name}</Text>
<Text className="text-sm text-gray-500">- {tool.description || "No description"}</Text>
<Text className="text-sm text-gray-500">
- {tool.description || t("mcpServerManagement.mcpToolPermissions.noDescription")}
</Text>
</div>
</div>
</div>
@ -224,7 +236,7 @@ const MCPToolPermissions: React.FC<MCPToolPermissionsProps> = ({
{/* Empty State */}
{!isLoading && !error && tools.length === 0 && (
<div className="text-center py-6">
<Text className="text-gray-500">No tools available</Text>
<Text className="text-gray-500">{t("mcpServerManagement.mcpToolPermissions.noToolsAvailable")}</Text>
</div>
)}
</div>

View file

@ -8409,5 +8409,18 @@
"maxSpendPlaceholder": "Max spend ($)",
"addBudgetWindow": "+ Add Budget Window"
}
},
"mcpServerManagement": {
"mcpToolPermissions": {
"failedToFetchTools": "Failed to fetch tools",
"riskGroups": "Risk Groups",
"flatList": "Flat List",
"selectAll": "Select All",
"deselectAll": "Deselect All",
"loadingTools": "Loading tools...",
"unableToLoadTools": "Unable to load tools",
"noDescription": "No description",
"noToolsAvailable": "No tools available"
}
}
}

View file

@ -8409,5 +8409,18 @@
"maxSpendPlaceholder": "最大花费 ($)",
"addBudgetWindow": "+ 添加预算窗口"
}
},
"mcpServerManagement": {
"mcpToolPermissions": {
"failedToFetchTools": "获取工具失败",
"riskGroups": "风险分组",
"flatList": "平铺列表",
"selectAll": "全选",
"deselectAll": "取消全选",
"loadingTools": "正在加载工具...",
"unableToLoadTools": "无法加载工具",
"noDescription": "无描述",
"noToolsAvailable": "暂无可用工具"
}
}
}