feat(ui): i18n for mcp_tools batch 6

This commit is contained in:
吹雪 2026-06-10 19:22:56 +09:00
parent 6b7e0f6bee
commit 4faf76ef3f
7 changed files with 378 additions and 142 deletions

View file

@ -4,6 +4,7 @@ import React, { useState } from "react";
import { Card, Typography, Space, Alert, Button, Switch, Form, Collapse } from "antd";
import { TabPanel, TabPanels, TabGroup, TabList, Tab, Title as TremorTitle, Text as TremorText } from "@tremor/react";
import { CopyIcon, Code, Terminal, Globe, CheckIcon, ExternalLinkIcon, KeyIcon, ServerIcon, Zap } from "lucide-react";
import { useTranslation } from "react-i18next";
import { getProxyBaseUrl } from "../networking";
import { copyToClipboard as utilCopyToClipboard } from "../../utils/dataUtils";
@ -34,6 +35,7 @@ const FeatureCard: React.FC<FeatureCardProps> = ({
serverName,
accessGroups = ["dev-group"],
}) => {
const { t } = useTranslation();
const [useServerHeader, setUseServerHeader] = useState(false);
const getHeadersConfig = () => {
@ -60,37 +62,42 @@ const FeatureCard: React.FC<FeatureCardProps> = ({
<Text className="text-gray-600">{description}</Text>
</div>
</div>
{serverName && (title === "Implementation Example" || title === "Configuration") && (
<Form.Item className="mb-4">
<div className="flex items-center gap-2 mb-2">
<Switch size="small" checked={useServerHeader} onChange={setUseServerHeader} />
<Text className="text-sm">
Limit tools to specific MCP servers or MCP groups by passing the <code>x-mcp-servers</code> header
</Text>
</div>
{useServerHeader && (
<Alert
className="mt-2"
type="info"
showIcon
message="Two Options"
description={
<div>
<p>
<strong>Option 1:</strong> Get a specific server: <code>"{serverName.replace(/\s+/g, "_")}"</code>
</p>
<p>
<strong>Option 2:</strong> Get a group of MCPs: <code>"dev-group"</code>
</p>
<p className="mt-2 text-sm text-gray-600">
You can also mix both: <code>"Server1,dev-group"</code>
</p>
</div>
}
/>
)}
</Form.Item>
)}
{serverName &&
(title === t("mcpTools.mcpConnect.implementationExample") ||
title === t("mcpTools.mcpConnect.configuration")) && (
<Form.Item className="mb-4">
<div className="flex items-center gap-2 mb-2">
<Switch size="small" checked={useServerHeader} onChange={setUseServerHeader} />
<Text className="text-sm">
{t("mcpTools.mcpConnect.limitToolsText")} <code>x-mcp-servers</code>{" "}
{t("mcpTools.mcpConnect.headerSuffix")}
</Text>
</div>
{useServerHeader && (
<Alert
className="mt-2"
type="info"
showIcon
message={t("mcpTools.mcpConnect.twoOptions")}
description={
<div>
<p>
<strong>{t("mcpTools.mcpConnect.option1Label")}</strong> {t("mcpTools.mcpConnect.option1Desc")}{" "}
<code>"{serverName.replace(/\s+/g, "_")}"</code>
</p>
<p>
<strong>{t("mcpTools.mcpConnect.option2Label")}</strong> {t("mcpTools.mcpConnect.option2Desc")}{" "}
<code>"dev-group"</code>
</p>
<p className="mt-2 text-sm text-gray-600">
{t("mcpTools.mcpConnect.mixBothText")} <code>"Server1,dev-group"</code>
</p>
</div>
}
/>
)}
</Form.Item>
)}
{React.Children.map(children, (child) => {
if (
React.isValidElement<CodeBlockProps>(child) &&
@ -115,6 +122,7 @@ interface MCPConnectProps {
}
const MCPConnect: React.FC<MCPConnectProps> = ({ currentServerAccessGroups = [] }) => {
const { t } = useTranslation();
const proxyBaseUrl = getProxyBaseUrl();
const [copiedStates, setCopiedStates] = useState<Record<string, boolean>>({});
const [serverHeaders, setServerHeaders] = useState<Record<string, string[]>>({
@ -209,40 +217,46 @@ const MCPConnect: React.FC<MCPConnectProps> = ({ currentServerAccessGroups = []
<div className="flex items-center gap-3 mb-3">
<Zap className="text-emerald-600" size={24} />
<Title level={4} className="mb-0 text-emerald-900">
LiteLLM Proxy API Integration
{t("mcpTools.mcpConnect.litellmProxyApiIntegration")}
</Title>
</div>
<Text className="text-emerald-700">
Connect to LiteLLM Proxy Responses API for seamless tool integration with multiple model providers
</Text>
<Text className="text-emerald-700">{t("mcpTools.mcpConnect.litellmProxyDesc")}</Text>
</div>
<Space direction="vertical" size="large" className="w-full">
<FeatureCard
icon={<KeyIcon className="text-emerald-600" size={16} />}
title="Virtual Key Setup"
description="Configure your LiteLLM Proxy Virtual Key for authentication"
title={t("mcpTools.mcpConnect.virtualKeySetup")}
description={t("mcpTools.mcpConnect.virtualKeySetupDesc")}
>
<Space direction="vertical" size="middle" className="w-full">
<div>
<Text>Get your Virtual Key from your LiteLLM Proxy dashboard or contact your administrator</Text>
<Text>{t("mcpTools.mcpConnect.getVirtualKeyText")}</Text>
</div>
<CodeBlock title="Environment Variable" code='export LITELLM_API_KEY="sk-..."' copyKey="litellm-env" />
<CodeBlock
title={t("mcpTools.mcpConnect.environmentVariable")}
code='export LITELLM_API_KEY="sk-..."'
copyKey="litellm-env"
/>
</Space>
</FeatureCard>
<FeatureCard
icon={<ServerIcon className="text-emerald-600" size={16} />}
title="MCP Server Information"
description="Connection details for your LiteLLM MCP server"
title={t("mcpTools.mcpConnect.mcpServerInformation")}
description={t("mcpTools.mcpConnect.mcpServerInformationDesc")}
>
<CodeBlock title="Server URL" code={`${proxyBaseUrl}/mcp`} copyKey="litellm-server-url" />
<CodeBlock
title={t("mcpTools.mcpConnect.serverUrl")}
code={`${proxyBaseUrl}/mcp`}
copyKey="litellm-server-url"
/>
</FeatureCard>
<FeatureCard
icon={<Code className="text-emerald-600" size={16} />}
title="Implementation Example"
description="Complete cURL example for using the LiteLLM Proxy Responses API"
title={t("mcpTools.mcpConnect.implementationExample")}
description={t("mcpTools.mcpConnect.litellmImplementationDesc")}
serverName={currentServer}
accessGroups={["dev-group"]}
>
@ -281,25 +295,23 @@ const MCPConnect: React.FC<MCPConnectProps> = ({ currentServerAccessGroups = []
<div className="flex items-center gap-3 mb-3">
<Code className="text-blue-600" size={24} />
<Title level={4} className="mb-0 text-blue-900">
OpenAI Responses API Integration
{t("mcpTools.mcpConnect.openaiResponsesApiIntegration")}
</Title>
</div>
<Text className="text-blue-700">
Connect OpenAI Responses API to your LiteLLM MCP server for seamless tool integration
</Text>
<Text className="text-blue-700">{t("mcpTools.mcpConnect.openaiDesc")}</Text>
</div>
<Space direction="vertical" size="large" className="w-full">
<FeatureCard
icon={<KeyIcon className="text-blue-600" size={16} />}
title="API Key Setup"
description="Configure your OpenAI API key for authentication"
title={t("mcpTools.mcpConnect.apiKeySetup")}
description={t("mcpTools.mcpConnect.apiKeySetupDesc")}
>
<Space direction="vertical" size="middle" className="w-full">
<div>
{/* eslint-disable-next-line react/no-unescaped-entities */}
<Text>
Get your API key from the{" "}
{t("mcpTools.mcpConnect.getApiKeyText")}{" "}
<a
href="https://platform.openai.com/api-keys"
target="_blank"
@ -310,22 +322,30 @@ const MCPConnect: React.FC<MCPConnectProps> = ({ currentServerAccessGroups = []
</a>
</Text>
</div>
<CodeBlock title="Environment Variable" code='export OPENAI_API_KEY="sk-..."' copyKey="openai-env" />
<CodeBlock
title={t("mcpTools.mcpConnect.environmentVariable")}
code='export OPENAI_API_KEY="sk-..."'
copyKey="openai-env"
/>
</Space>
</FeatureCard>
<FeatureCard
icon={<ServerIcon className="text-blue-600" size={16} />}
title="MCP Server Information"
description="Connection details for your LiteLLM MCP server"
title={t("mcpTools.mcpConnect.mcpServerInformation")}
description={t("mcpTools.mcpConnect.mcpServerInformationDesc")}
>
<CodeBlock title="Server URL" code={`${proxyBaseUrl}/mcp`} copyKey="openai-server-url" />
<CodeBlock
title={t("mcpTools.mcpConnect.serverUrl")}
code={`${proxyBaseUrl}/mcp`}
copyKey="openai-server-url"
/>
</FeatureCard>
<FeatureCard
icon={<Code className="text-blue-600" size={16} />}
title="Implementation Example"
description="Complete cURL example for using the Responses API"
title={t("mcpTools.mcpConnect.implementationExample")}
description={t("mcpTools.mcpConnect.openaiImplementationDesc")}
serverName="Zapier Gmail"
accessGroups={["dev-group"]}
>
@ -364,41 +384,38 @@ const MCPConnect: React.FC<MCPConnectProps> = ({ currentServerAccessGroups = []
<div className="flex items-center gap-3 mb-3">
<Terminal className="text-purple-600" size={24} />
<Title level={4} className="mb-0 text-purple-900">
Cursor IDE Integration
{t("mcpTools.mcpConnect.cursorIdeIntegration")}
</Title>
</div>
<Text className="text-purple-700">
Use tools directly from Cursor IDE with LiteLLM MCP. Enable your AI assistant to perform real-world tasks
without leaving your coding environment.
</Text>
<Text className="text-purple-700">{t("mcpTools.mcpConnect.cursorDesc")}</Text>
</div>
<Card className="border border-gray-200">
<Title level={5} className="mb-4 text-gray-800">
Setup Instructions
{t("mcpTools.mcpConnect.setupInstructions")}
</Title>
<Space direction="vertical" size="large" className="w-full">
<StepCard step={1} title="Open Cursor Settings">
<StepCard step={1} title={t("mcpTools.mcpConnect.step1Title")}>
<Text className="text-gray-600">
Use the keyboard shortcut <code className="bg-gray-100 px-2 py-1 rounded">⇧+⌘+J</code> (Mac) or{" "}
<code className="bg-gray-100 px-2 py-1 rounded">Ctrl+Shift+J</code> (Windows/Linux)
{t("mcpTools.mcpConnect.step1Desc")} <code className="bg-gray-100 px-2 py-1 rounded">⇧+⌘+J</code> (Mac){" "}
{t("mcpTools.mcpConnect.orText")} <code className="bg-gray-100 px-2 py-1 rounded">Ctrl+Shift+J</code>{" "}
(Windows/Linux)
</Text>
</StepCard>
<StepCard step={2} title="Navigate to MCP Tools">
<Text className="text-gray-600">Go to the "MCP Tools" tab and click "New MCP Server"</Text>
<StepCard step={2} title={t("mcpTools.mcpConnect.step2Title")}>
<Text className="text-gray-600">{t("mcpTools.mcpConnect.step2Desc")}</Text>
</StepCard>
<StepCard step={3} title="Add Configuration">
<StepCard step={3} title={t("mcpTools.mcpConnect.step3Title")}>
<Text className="text-gray-600 mb-3">
Copy the JSON configuration below and paste it into Cursor, then save with{" "}
<code className="bg-gray-100 px-2 py-1 rounded">Cmd+S</code> or{" "}
<code className="bg-gray-100 px-2 py-1 rounded">Ctrl+S</code>
{t("mcpTools.mcpConnect.step3Desc")} <code className="bg-gray-100 px-2 py-1 rounded">Cmd+S</code>{" "}
{t("mcpTools.mcpConnect.orText")} <code className="bg-gray-100 px-2 py-1 rounded">Ctrl+S</code>
</Text>
<FeatureCard
icon={<Code className="text-purple-600" size={16} />}
title="Configuration"
description="Cursor MCP configuration"
title={t("mcpTools.mcpConnect.configuration")}
description={t("mcpTools.mcpConnect.cursorConfigDesc")}
serverName="Zapier Gmail"
accessGroups={["dev-group"]}
>
@ -430,29 +447,28 @@ const MCPConnect: React.FC<MCPConnectProps> = ({ currentServerAccessGroups = []
<div className="flex items-center gap-3 mb-3">
<Globe className="text-green-600" size={24} />
<Title level={4} className="mb-0 text-green-900">
Streamable HTTP Transport
{t("mcpTools.mcpConnect.streamableHttpTransport")}
</Title>
</div>
<Text className="text-green-700">
Connect to LiteLLM MCP using HTTP transport. Compatible with any MCP client that supports HTTP streaming.
</Text>
<Text className="text-green-700">{t("mcpTools.mcpConnect.streamableHttpDesc")}</Text>
</div>
<FeatureCard
icon={<Globe className="text-green-600" size={16} />}
title="Universal MCP Connection"
description="Use this URL with any MCP client that supports HTTP transport"
title={t("mcpTools.mcpConnect.universalMcpConnection")}
description={t("mcpTools.mcpConnect.universalMcpConnectionDesc")}
>
<Space direction="vertical" size="middle" className="w-full">
<div>
<Text>
Each MCP client supports different transports. Refer to your client documentation to determine the
appropriate transport method.
</Text>
<Text>{t("mcpTools.mcpConnect.transportMethodText")}</Text>
</div>
<CodeBlock title="Server URL" code={`${proxyBaseUrl}/mcp`} copyKey="http-server-url" />
<CodeBlock
title="Headers Configuration"
title={t("mcpTools.mcpConnect.serverUrl")}
code={`${proxyBaseUrl}/mcp`}
copyKey="http-server-url"
/>
<CodeBlock
title={t("mcpTools.mcpConnect.headersConfiguration")}
code={JSON.stringify(
{
"x-litellm-api-key": "Bearer YOUR_LITELLM_API_KEY",
@ -469,7 +485,7 @@ const MCPConnect: React.FC<MCPConnectProps> = ({ currentServerAccessGroups = []
href="https://modelcontextprotocol.io/docs/concepts/transports"
icon={<ExternalLinkIcon size={14} />}
>
Learn more about MCP transports
{t("mcpTools.mcpConnect.learnMoreTransports")}
</Button>
</div>
</Space>
@ -481,11 +497,10 @@ const MCPConnect: React.FC<MCPConnectProps> = ({ currentServerAccessGroups = []
<div>
<Space direction="vertical" size="large" className="w-full">
<div>
<TremorTitle className="text-3xl font-bold text-gray-900 mb-3">Connect to your MCP client</TremorTitle>
<TremorText className="text-lg text-gray-600">
Use tools directly from any MCP client with LiteLLM MCP. Enable your AI assistant to perform real-world
tasks through a simple, secure connection.
</TremorText>
<TremorTitle className="text-3xl font-bold text-gray-900 mb-3">
{t("mcpTools.mcpConnect.pageTitle")}
</TremorTitle>
<TremorText className="text-lg text-gray-600">{t("mcpTools.mcpConnect.pageDesc")}</TremorText>
</div>
<TabGroup className="w-full">
@ -494,25 +509,25 @@ const MCPConnect: React.FC<MCPConnectProps> = ({ currentServerAccessGroups = []
<Tab className="px-6 py-3 rounded-md transition-all duration-200">
<span className="flex items-center gap-2 font-medium">
<Code size={18} />
OpenAI API
{t("mcpTools.mcpConnect.tabOpenAI")}
</span>
</Tab>
<Tab className="px-6 py-3 rounded-md transition-all duration-200">
<span className="flex items-center gap-2 font-medium">
<Zap size={18} />
LiteLLM Proxy
{t("mcpTools.mcpConnect.tabLiteLLMProxy")}
</span>
</Tab>
<Tab className="px-6 py-3 rounded-md transition-all duration-200">
<span className="flex items-center gap-2 font-medium">
<Terminal size={18} />
Cursor
{t("mcpTools.mcpConnect.tabCursor")}
</span>
</Tab>
<Tab className="px-6 py-3 rounded-md transition-all duration-200">
<span className="flex items-center gap-2 font-medium">
<Globe size={18} />
Streamable HTTP
{t("mcpTools.mcpConnect.tabStreamableHttp")}
</span>
</Tab>
</div>

View file

@ -1,6 +1,7 @@
import React from "react";
import { Button, Spin, Alert, Collapse } from "antd";
import { CheckCircleOutlined, ExclamationCircleOutlined, ReloadOutlined, ToolOutlined } from "@ant-design/icons";
import { useTranslation } from "react-i18next";
import { Card, Title, Text } from "@tremor/react";
interface MCPConnectionStatusProps {
@ -22,7 +23,7 @@ const MCPConnectionStatus: React.FC<MCPConnectionStatusProps> = ({
canFetchTools,
fetchTools,
}) => {
// Don't show anything if required fields aren't filled
const { t } = useTranslation();
if (!canFetchTools && !formValues.url && !formValues.spec_path) {
return null;
}
@ -32,15 +33,15 @@ const MCPConnectionStatus: React.FC<MCPConnectionStatusProps> = ({
<div className="space-y-4">
<div className="flex items-center gap-2">
<CheckCircleOutlined className="text-blue-600" />
<Title>Connection Status</Title>
<Title>{t("mcpTools.mcpConnectionStatus.title")}</Title>
</div>
{!canFetchTools && (formValues.url || formValues.spec_path) && (
<div className="text-center py-6 text-gray-400 border rounded-lg border-dashed">
<ToolOutlined className="text-2xl mb-2" />
<Text>Complete required fields to test connection</Text>
<Text>{t("mcpTools.mcpConnectionStatus.completeRequiredFields")}</Text>
<br />
<Text className="text-sm">Fill in URL, Transport, and Authentication to test MCP server connection</Text>
<Text className="text-sm">{t("mcpTools.mcpConnectionStatus.fillInRequiredFields")}</Text>
</div>
)}
@ -50,35 +51,37 @@ const MCPConnectionStatus: React.FC<MCPConnectionStatusProps> = ({
<div>
<Text className="text-gray-700 font-medium">
{isLoadingTools
? "Testing connection to MCP server..."
? t("mcpTools.mcpConnectionStatus.testingConnection")
: tools.length > 0
? "Connection successful"
? t("mcpTools.mcpConnectionStatus.connectionSuccessful")
: toolsError
? "Connection failed"
: "Ready to test connection"}
? t("mcpTools.mcpConnectionStatus.connectionFailed")
: t("mcpTools.mcpConnectionStatus.readyToTest")}
</Text>
<br />
<Text className="text-gray-500 text-sm">Server: {formValues.url || formValues.spec_path}</Text>
<Text className="text-gray-500 text-sm">
{t("mcpTools.mcpConnectionStatus.serverLabel", { server: formValues.url || formValues.spec_path })}
</Text>
</div>
{isLoadingTools && (
<div className="flex items-center text-blue-600">
<Spin size="small" className="mr-2" />
<Text className="text-blue-600">Connecting...</Text>
<Text className="text-blue-600">{t("mcpTools.mcpConnectionStatus.connecting")}</Text>
</div>
)}
{!isLoadingTools && !toolsError && tools.length > 0 && (
<div className="flex items-center text-green-600">
<CheckCircleOutlined className="mr-1" />
<Text className="text-green-600 font-medium">Connected</Text>
<Text className="text-green-600 font-medium">{t("mcpTools.mcpConnectionStatus.connected")}</Text>
</div>
)}
{toolsError && (
<div className="flex items-center text-red-600">
<ExclamationCircleOutlined className="mr-1" />
<Text className="text-red-600 font-medium">Failed</Text>
<Text className="text-red-600 font-medium">{t("mcpTools.mcpConnectionStatus.failed")}</Text>
</div>
)}
</div>
@ -86,13 +89,13 @@ const MCPConnectionStatus: React.FC<MCPConnectionStatusProps> = ({
{isLoadingTools && (
<div className="flex items-center justify-center py-6">
<Spin size="large" />
<Text className="ml-3">Testing connection and loading tools...</Text>
<Text className="ml-3">{t("mcpTools.mcpConnectionStatus.testingAndLoading")}</Text>
</div>
)}
{toolsError && (
<Alert
message="Connection Failed"
message={t("mcpTools.mcpConnectionStatus.connectionFailedAlert")}
description={
<div>
<div>{toolsError}</div>
@ -101,7 +104,7 @@ const MCPConnectionStatus: React.FC<MCPConnectionStatusProps> = ({
items={[
{
key: "stack-trace",
label: "Stack Trace",
label: t("mcpTools.mcpConnectionStatus.stackTrace"),
children: (
<pre
style={{
@ -131,7 +134,7 @@ const MCPConnectionStatus: React.FC<MCPConnectionStatusProps> = ({
showIcon
action={
<Button icon={<ReloadOutlined />} onClick={fetchTools} size="small">
Retry
{t("common.retry")}
</Button>
}
/>
@ -140,9 +143,11 @@ const MCPConnectionStatus: React.FC<MCPConnectionStatusProps> = ({
{!isLoadingTools && tools.length === 0 && !toolsError && (
<div className="text-center py-6 text-gray-500 border rounded-lg border-dashed">
<CheckCircleOutlined className="text-2xl mb-2 text-green-500" />
<Text className="text-green-600 font-medium">Connection successful!</Text>
<Text className="text-green-600 font-medium">
{t("mcpTools.mcpConnectionStatus.connectionSuccessfulBang")}
</Text>
<br />
<Text className="text-gray-500">No tools found for this MCP server</Text>
<Text className="text-gray-500">{t("mcpTools.mcpConnectionStatus.noToolsFound")}</Text>
</div>
)}
</div>

View file

@ -1,5 +1,6 @@
import React, { useState, useMemo, useEffect } from "react";
import { Modal, Input, Typography } from "antd";
import { useTranslation } from "react-i18next";
import { fetchDiscoverableMCPServers } from "../networking";
import { DiscoverableMCPServer, DiscoverMCPServersResponse } from "./types";
import { mcpLogoImg } from "./create_mcp_server";
@ -30,6 +31,7 @@ const MCPDiscovery: React.FC<MCPDiscoveryProps> = ({
onCustomServer,
accessToken,
}) => {
const { t } = useTranslation();
const [servers, setServers] = useState<DiscoverableMCPServer[]>([]);
const [categories, setCategories] = useState<string[]>([]);
const [loading, setLoading] = useState(false);
@ -47,7 +49,7 @@ const MCPDiscovery: React.FC<MCPDiscoveryProps> = ({
setCategories(data.categories || []);
})
.catch((err: Error) => {
setError(err.message || "Failed to load MCP servers");
setError(err.message || t("mcpTools.mcpDiscovery.failedToLoadServers"));
})
.finally(() => {
setLoading(false);
@ -105,13 +107,13 @@ const MCPDiscovery: React.FC<MCPDiscoveryProps> = ({
objectFit: "contain",
}}
/>
<h2 className="text-xl font-semibold text-gray-900">Add MCP Server</h2>
<h2 className="text-xl font-semibold text-gray-900">{t("mcpTools.mcpDiscovery.title")}</h2>
</div>
<button
onClick={onCustomServer}
className="text-sm text-blue-600 hover:text-blue-800 cursor-pointer bg-transparent border-none font-medium"
>
+ Custom Server
{t("mcpTools.mcpDiscovery.customServer")}
</button>
</div>
}
@ -153,7 +155,7 @@ const MCPDiscovery: React.FC<MCPDiscoveryProps> = ({
{/* Search */}
<Search
placeholder="Search servers..."
placeholder={t("mcpTools.mcpDiscovery.searchPlaceholder")}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
style={{ marginBottom: 16 }}
@ -178,16 +180,16 @@ const MCPDiscovery: React.FC<MCPDiscoveryProps> = ({
{error && (
<div style={{ textAlign: "center", padding: "32px 0", color: "#9ca3af" }}>
<Text>Failed to load servers: {error}</Text>
<Text>{t("mcpTools.mcpDiscovery.failedToLoadServersError", { error })}</Text>
</div>
)}
{!loading && !error && filteredServers.length === 0 && (
<div style={{ textAlign: "center", padding: "32px 0", color: "#9ca3af" }}>
<Text>
No servers found.{" "}
{t("mcpTools.mcpDiscovery.noServersFound")}{" "}
<a onClick={onCustomServer} style={{ color: "#2563eb", cursor: "pointer" }}>
Add a custom server
{t("mcpTools.mcpDiscovery.addCustomServer")}
</a>
</Text>
</div>

View file

@ -1,6 +1,7 @@
import React from "react";
import { Tooltip, InputNumber, Collapse, Badge } from "antd";
import { InfoCircleOutlined, DollarOutlined, ToolOutlined } from "@ant-design/icons";
import { useTranslation } from "react-i18next";
import { Card, Title, Text } from "@tremor/react";
import { MCPServerCostInfo } from "./types";
@ -17,6 +18,7 @@ const MCPServerCostConfig: React.FC<MCPServerCostConfigProps> = ({
tools = [],
disabled = false,
}) => {
const { t } = useTranslation();
const handleDefaultCostChange = (defaultCost: number | null) => {
const updated = {
...value,
@ -41,8 +43,8 @@ const MCPServerCostConfig: React.FC<MCPServerCostConfigProps> = ({
<div className="space-y-6">
<div className="flex items-center gap-2 mb-4">
<DollarOutlined className="text-green-600" />
<Title>Cost Configuration</Title>
<Tooltip title="Configure costs for this MCP server's tool calls. Set a default rate and per-tool overrides.">
<Title>{t("mcpTools.mcpServerCostConfig.title")}</Title>
<Tooltip title={t("mcpTools.mcpServerCostConfig.titleTooltip")}>
<InfoCircleOutlined className="text-gray-400" />
</Tooltip>
</div>
@ -50,8 +52,8 @@ const MCPServerCostConfig: React.FC<MCPServerCostConfigProps> = ({
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Default Cost per Query ($)
<Tooltip title="Default cost charged for each tool call to this server.">
{t("mcpTools.mcpServerCostConfig.defaultCostLabel")}
<Tooltip title={t("mcpTools.mcpServerCostConfig.defaultCostTooltip")}>
<InfoCircleOutlined className="ml-1 text-gray-400" />
</Tooltip>
</label>
@ -67,15 +69,15 @@ const MCPServerCostConfig: React.FC<MCPServerCostConfigProps> = ({
addonBefore="$"
/>
<Text className="block mt-1 text-gray-500 text-sm">
Set a default cost for all tool calls to this server
{t("mcpTools.mcpServerCostConfig.defaultCostHelp")}
</Text>
</div>
{tools.length > 0 && (
<div className="space-y-4">
<label className="block text-sm font-medium text-gray-700">
Tool-Specific Costs ($)
<Tooltip title="Override the default cost for specific tools. Leave blank to use the default rate.">
{t("mcpTools.mcpServerCostConfig.toolSpecificCostsLabel")}
<Tooltip title={t("mcpTools.mcpServerCostConfig.toolSpecificCostsTooltip")}>
<InfoCircleOutlined className="ml-1 text-gray-400" />
</Tooltip>
</label>
@ -86,7 +88,7 @@ const MCPServerCostConfig: React.FC<MCPServerCostConfigProps> = ({
label: (
<div className="flex items-center">
<ToolOutlined className="mr-2 text-blue-500" />
<span className="font-medium">Available Tools</span>
<span className="font-medium">{t("mcpTools.mcpServerCostConfig.availableTools")}</span>
<Badge
count={tools.length}
style={{
@ -111,7 +113,7 @@ const MCPServerCostConfig: React.FC<MCPServerCostConfigProps> = ({
min={0}
step={0.0001}
precision={4}
placeholder="Use default"
placeholder={t("mcpTools.mcpServerCostConfig.useDefaultPlaceholder")}
value={value.tool_name_to_cost_per_query?.[tool.name]}
onChange={(cost) => handleToolCostChange(tool.name, cost)}
disabled={disabled}
@ -133,11 +135,11 @@ const MCPServerCostConfig: React.FC<MCPServerCostConfigProps> = ({
{(value.default_cost_per_query ||
(value.tool_name_to_cost_per_query && Object.keys(value.tool_name_to_cost_per_query).length > 0)) && (
<div className="mt-6 p-4 bg-blue-50 border border-blue-200 rounded-lg">
<Text className="text-blue-800 font-medium">Cost Summary:</Text>
<Text className="text-blue-800 font-medium">{t("mcpTools.mcpServerCostConfig.costSummary")}</Text>
<div className="mt-2 space-y-1">
{value.default_cost_per_query && (
<Text className="text-blue-700">
• Default cost: ${value.default_cost_per_query.toFixed(4)} per query
{t("mcpTools.mcpServerCostConfig.defaultCostRow", { cost: value.default_cost_per_query.toFixed(4) })}
</Text>
)}
{value.tool_name_to_cost_per_query &&
@ -146,7 +148,7 @@ const MCPServerCostConfig: React.FC<MCPServerCostConfigProps> = ({
cost !== null &&
cost !== undefined && (
<Text key={toolName} className="text-blue-700">
• {toolName}: ${cost.toFixed(4)} per query
{t("mcpTools.mcpServerCostConfig.toolCostRow", { toolName, cost: cost.toFixed(4) })}
</Text>
),
)}

View file

@ -1,4 +1,5 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { Text } from "@tremor/react";
import { MCPServerCostInfo } from "./types";
@ -7,6 +8,7 @@ interface MCPServerCostDisplayProps {
}
const MCPServerCostDisplay: React.FC<MCPServerCostDisplayProps> = ({ costConfig }) => {
const { t } = useTranslation();
const hasDefaultCost =
costConfig?.default_cost_per_query !== undefined && costConfig?.default_cost_per_query !== null;
const hasToolCosts =
@ -18,9 +20,7 @@ const MCPServerCostDisplay: React.FC<MCPServerCostDisplayProps> = ({ costConfig
<div className="mt-6 pt-6 border-t border-gray-200">
<div className="space-y-4">
<div className="p-4 bg-gray-50 border border-gray-200 rounded-lg">
<Text className="text-gray-600">
No cost configuration set for this server. Tool calls will be charged at $0.00 per tool call.
</Text>
<Text className="text-gray-600">{t("mcpTools.mcpServerCostDisplay.noCostConfig")}</Text>
</div>
</div>
</div>
@ -34,14 +34,14 @@ const MCPServerCostDisplay: React.FC<MCPServerCostDisplayProps> = ({ costConfig
costConfig?.default_cost_per_query !== undefined &&
costConfig?.default_cost_per_query !== null && (
<div>
<Text className="font-medium">Default Cost per Query</Text>
<Text className="font-medium">{t("mcpTools.mcpServerCostDisplay.defaultCostPerQuery")}</Text>
<div className="text-green-600 font-mono">${costConfig.default_cost_per_query.toFixed(4)}</div>
</div>
)}
{hasToolCosts && costConfig?.tool_name_to_cost_per_query && (
<div>
<Text className="font-medium">Tool-Specific Costs</Text>
<Text className="font-medium">{t("mcpTools.mcpServerCostDisplay.toolSpecificCosts")}</Text>
<div className="mt-2 space-y-2">
{Object.entries(costConfig.tool_name_to_cost_per_query).map(
([toolName, cost]) =>
@ -49,7 +49,9 @@ const MCPServerCostDisplay: React.FC<MCPServerCostDisplayProps> = ({ costConfig
cost !== undefined && (
<div key={toolName} className="flex justify-between items-center p-3 bg-gray-50 rounded-lg">
<Text className="font-medium">{toolName}</Text>
<Text className="text-green-600 font-mono">${cost.toFixed(4)} per query</Text>
<Text className="text-green-600 font-mono">
{t("mcpTools.mcpServerCostDisplay.costPerQuery", { cost: cost.toFixed(4) })}
</Text>
</div>
),
)}
@ -58,18 +60,22 @@ const MCPServerCostDisplay: React.FC<MCPServerCostDisplayProps> = ({ costConfig
)}
<div className="mt-4 p-4 bg-blue-50 border border-blue-200 rounded-lg">
<Text className="text-blue-800 font-medium">Cost Summary:</Text>
<Text className="text-blue-800 font-medium">{t("mcpTools.mcpServerCostDisplay.costSummary")}</Text>
<div className="mt-2 space-y-1">
{hasDefaultCost &&
costConfig?.default_cost_per_query !== undefined &&
costConfig?.default_cost_per_query !== null && (
<Text className="text-blue-700">
• Default cost: ${costConfig.default_cost_per_query.toFixed(4)} per query
{t("mcpTools.mcpServerCostDisplay.defaultCostRow", {
cost: costConfig.default_cost_per_query.toFixed(4),
})}
</Text>
)}
{hasToolCosts && costConfig?.tool_name_to_cost_per_query && (
<Text className="text-blue-700">
• {Object.keys(costConfig.tool_name_to_cost_per_query).length} tool(s) with custom pricing
{t("mcpTools.mcpServerCostDisplay.toolsWithCustomPricing", {
count: Object.keys(costConfig.tool_name_to_cost_per_query).length,
})}
</Text>
)}
</div>

View file

@ -3420,6 +3420,109 @@
"creating": "Creating...",
"addButton": "Add MCP Server"
},
"mcpConnect": {
"pageTitle": "Connect to your MCP client",
"pageDesc": "Use tools directly from any MCP client with LiteLLM MCP. Enable your AI assistant to perform real-world tasks through a simple, secure connection.",
"tabOpenAI": "OpenAI API",
"tabLiteLLMProxy": "LiteLLM Proxy",
"tabCursor": "Cursor",
"tabStreamableHttp": "Streamable HTTP",
"implementationExample": "Implementation Example",
"configuration": "Configuration",
"limitToolsText": "Limit tools to specific MCP servers or MCP groups by passing the",
"headerSuffix": "header",
"twoOptions": "Two Options",
"option1Label": "Option 1:",
"option1Desc": "Get a specific server:",
"option2Label": "Option 2:",
"option2Desc": "Get a group of MCPs:",
"mixBothText": "You can also mix both:",
"litellmProxyApiIntegration": "LiteLLM Proxy API Integration",
"litellmProxyDesc": "Connect to LiteLLM Proxy Responses API for seamless tool integration with multiple model providers",
"virtualKeySetup": "Virtual Key Setup",
"virtualKeySetupDesc": "Configure your LiteLLM Proxy Virtual Key for authentication",
"getVirtualKeyText": "Get your Virtual Key from your LiteLLM Proxy dashboard or contact your administrator",
"environmentVariable": "Environment Variable",
"mcpServerInformation": "MCP Server Information",
"mcpServerInformationDesc": "Connection details for your LiteLLM MCP server",
"serverUrl": "Server URL",
"litellmImplementationDesc": "Complete cURL example for using the LiteLLM Proxy Responses API",
"openaiResponsesApiIntegration": "OpenAI Responses API Integration",
"openaiDesc": "Connect OpenAI Responses API to your LiteLLM MCP server for seamless tool integration",
"apiKeySetup": "API Key Setup",
"apiKeySetupDesc": "Configure your OpenAI API key for authentication",
"getApiKeyText": "Get your API key from the",
"openaiImplementationDesc": "Complete cURL example for using the Responses API",
"cursorIdeIntegration": "Cursor IDE Integration",
"cursorDesc": "Use tools directly from Cursor IDE with LiteLLM MCP. Enable your AI assistant to perform real-world tasks without leaving your coding environment.",
"setupInstructions": "Setup Instructions",
"step1Title": "Open Cursor Settings",
"step1Desc": "Use the keyboard shortcut",
"orText": "or",
"step2Title": "Navigate to MCP Tools",
"step2Desc": "Go to the \"MCP Tools\" tab and click \"New MCP Server\"",
"step3Title": "Add Configuration",
"step3Desc": "Copy the JSON configuration below and paste it into Cursor, then save with",
"cursorConfigDesc": "Cursor MCP configuration",
"streamableHttpTransport": "Streamable HTTP Transport",
"streamableHttpDesc": "Connect to LiteLLM MCP using HTTP transport. Compatible with any MCP client that supports HTTP streaming.",
"universalMcpConnection": "Universal MCP Connection",
"universalMcpConnectionDesc": "Use this URL with any MCP client that supports HTTP transport",
"transportMethodText": "Each MCP client supports different transports. Refer to your client documentation to determine the appropriate transport method.",
"headersConfiguration": "Headers Configuration",
"learnMoreTransports": "Learn more about MCP transports"
},
"mcpConnectionStatus": {
"title": "Connection Status",
"completeRequiredFields": "Complete required fields to test connection",
"fillInRequiredFields": "Fill in URL, Transport, and Authentication to test MCP server connection",
"testingConnection": "Testing connection to MCP server...",
"connectionSuccessful": "Connection successful",
"connectionFailed": "Connection failed",
"readyToTest": "Ready to test connection",
"serverLabel": "Server: {{server}}",
"connecting": "Connecting...",
"connected": "Connected",
"failed": "Failed",
"testingAndLoading": "Testing connection and loading tools...",
"connectionFailedAlert": "Connection Failed",
"stackTrace": "Stack Trace",
"connectionSuccessfulBang": "Connection successful!",
"noToolsFound": "No tools found for this MCP server"
},
"mcpDiscovery": {
"title": "Add MCP Server",
"customServer": "+ Custom Server",
"searchPlaceholder": "Search servers...",
"failedToLoadServers": "Failed to load MCP servers",
"failedToLoadServersError": "Failed to load servers: {{error}}",
"noServersFound": "No servers found.",
"addCustomServer": "Add a custom server"
},
"mcpServerCostConfig": {
"title": "Cost Configuration",
"titleTooltip": "Configure costs for this MCP server's tool calls. Set a default rate and per-tool overrides.",
"defaultCostLabel": "Default Cost per Query ($)",
"defaultCostTooltip": "Default cost charged for each tool call to this server.",
"defaultCostHelp": "Set a default cost for all tool calls to this server",
"toolSpecificCostsLabel": "Tool-Specific Costs ($)",
"toolSpecificCostsTooltip": "Override the default cost for specific tools. Leave blank to use the default rate.",
"availableTools": "Available Tools",
"useDefaultPlaceholder": "Use default",
"costSummary": "Cost Summary:",
"defaultCostRow": "• Default cost: ${{cost}} per query",
"toolCostRow": "• {{toolName}}: ${{cost}} per query"
},
"mcpServerCostDisplay": {
"noCostConfig": "No cost configuration set for this server. Tool calls will be charged at $0.00 per tool call.",
"defaultCostPerQuery": "Default Cost per Query",
"toolSpecificCosts": "Tool-Specific Costs",
"costPerQuery": "${{cost}} per query",
"costSummary": "Cost Summary:",
"defaultCostRow": "• Default cost: ${{cost}} per query",
"toolsWithCustomPricing_one": "• {{count}} tool with custom pricing",
"toolsWithCustomPricing_other": "• {{count}} tools with custom pricing"
},
"toolTestPanel": {
"testToolTitle": "Test Tool:",
"clickToCopyToolName": "Click to copy tool name",

View file

@ -3420,6 +3420,109 @@
"creating": "创建中...",
"addButton": "添加 MCP 服务器"
},
"mcpConnect": {
"pageTitle": "连接到您的 MCP 客户端",
"pageDesc": "通过 LiteLLM MCP 直接在任意 MCP 客户端中使用工具,让您的 AI 助手通过简单、安全的连接执行现实任务。",
"tabOpenAI": "OpenAI API",
"tabLiteLLMProxy": "LiteLLM Proxy",
"tabCursor": "Cursor",
"tabStreamableHttp": "Streamable HTTP",
"implementationExample": "Implementation Example",
"configuration": "Configuration",
"limitToolsText": "通过传递",
"headerSuffix": "请求头,将工具限定到特定 MCP 服务器或 MCP 组",
"twoOptions": "两种方式",
"option1Label": "方式一:",
"option1Desc": "指定单个服务器:",
"option2Label": "方式二:",
"option2Desc": "指定一组 MCP:",
"mixBothText": "也可以混合使用:",
"litellmProxyApiIntegration": "LiteLLM Proxy API 集成",
"litellmProxyDesc": "连接 LiteLLM Proxy Responses API,与多个模型提供商无缝集成工具",
"virtualKeySetup": "虚拟密钥配置",
"virtualKeySetupDesc": "配置您的 LiteLLM Proxy 虚拟密钥以完成身份认证",
"getVirtualKeyText": "从您的 LiteLLM Proxy 仪表盘获取虚拟密钥,或联系您的管理员",
"environmentVariable": "环境变量",
"mcpServerInformation": "MCP 服务器信息",
"mcpServerInformationDesc": "您的 LiteLLM MCP 服务器连接详情",
"serverUrl": "服务器 URL",
"litellmImplementationDesc": "使用 LiteLLM Proxy Responses API 的完整 cURL 示例",
"openaiResponsesApiIntegration": "OpenAI Responses API 集成",
"openaiDesc": "将 OpenAI Responses API 连接到您的 LiteLLM MCP 服务器,实现无缝工具集成",
"apiKeySetup": "API 密钥配置",
"apiKeySetupDesc": "配置您的 OpenAI API 密钥以完成身份认证",
"getApiKeyText": "从以下位置获取您的 API 密钥:",
"openaiImplementationDesc": "使用 Responses API 的完整 cURL 示例",
"cursorIdeIntegration": "Cursor IDE 集成",
"cursorDesc": "通过 LiteLLM MCP 直接在 Cursor IDE 中使用工具。让您的 AI 助手无需离开编码环境即可执行现实任务。",
"setupInstructions": "配置说明",
"step1Title": "打开 Cursor 设置",
"step1Desc": "使用键盘快捷键",
"orText": "或",
"step2Title": "导航至 MCP 工具",
"step2Desc": "前往「MCP Tools」标签页,点击「New MCP Server」",
"step3Title": "添加配置",
"step3Desc": "复制下方 JSON 配置并粘贴到 Cursor,然后按",
"cursorConfigDesc": "Cursor MCP 配置",
"streamableHttpTransport": "Streamable HTTP 传输",
"streamableHttpDesc": "通过 HTTP 传输连接 LiteLLM MCP,兼容任何支持 HTTP 流式传输的 MCP 客户端。",
"universalMcpConnection": "通用 MCP 连接",
"universalMcpConnectionDesc": "使用此 URL 连接任何支持 HTTP 传输的 MCP 客户端",
"transportMethodText": "不同 MCP 客户端支持的传输方式各不相同,请参阅您的客户端文档以确定合适的传输方式。",
"headersConfiguration": "请求头配置",
"learnMoreTransports": "了解更多关于 MCP 传输的信息"
},
"mcpConnectionStatus": {
"title": "连接状态",
"completeRequiredFields": "请填写必填字段以测试连接",
"fillInRequiredFields": "请填写 URL、传输方式和认证信息以测试 MCP 服务器连接",
"testingConnection": "正在测试与 MCP 服务器的连接...",
"connectionSuccessful": "连接成功",
"connectionFailed": "连接失败",
"readyToTest": "准备测试连接",
"serverLabel": "服务器:{{server}}",
"connecting": "连接中...",
"connected": "已连接",
"failed": "失败",
"testingAndLoading": "正在测试连接并加载工具...",
"connectionFailedAlert": "连接失败",
"stackTrace": "堆栈跟踪",
"connectionSuccessfulBang": "连接成功!",
"noToolsFound": "此 MCP 服务器未发现任何工具"
},
"mcpDiscovery": {
"title": "添加 MCP 服务器",
"customServer": "+ 自定义服务器",
"searchPlaceholder": "搜索服务器...",
"failedToLoadServers": "加载 MCP 服务器失败",
"failedToLoadServersError": "加载服务器失败:{{error}}",
"noServersFound": "未找到服务器。",
"addCustomServer": "添加自定义服务器"
},
"mcpServerCostConfig": {
"title": "费用配置",
"titleTooltip": "配置此 MCP 服务器工具调用的费用。设置默认费率及每个工具的覆盖费率。",
"defaultCostLabel": "每次查询默认费用($)",
"defaultCostTooltip": "此服务器每次工具调用收取的默认费用。",
"defaultCostHelp": "为此服务器的所有工具调用设置默认费用",
"toolSpecificCostsLabel": "工具专属费用($)",
"toolSpecificCostsTooltip": "为特定工具覆盖默认费用。留空则使用默认费率。",
"availableTools": "可用工具",
"useDefaultPlaceholder": "使用默认值",
"costSummary": "费用汇总:",
"defaultCostRow": "• 默认费用:${{cost}} / 次查询",
"toolCostRow": "• {{toolName}}:${{cost}} / 次查询"
},
"mcpServerCostDisplay": {
"noCostConfig": "此服务器未配置费用。工具调用将按每次 $0.00 收费。",
"defaultCostPerQuery": "每次查询默认费用",
"toolSpecificCosts": "工具专属费用",
"costPerQuery": "${{cost}} / 次查询",
"costSummary": "费用汇总:",
"defaultCostRow": "• 默认费用:${{cost}} / 次查询",
"toolsWithCustomPricing_one": "• {{count}} 个工具使用自定义定价",
"toolsWithCustomPricing_other": "• {{count}} 个工具使用自定义定价"
},
"toolTestPanel": {
"testToolTitle": "测试工具:",
"clickToCopyToolName": "点击复制工具名称",