Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions webview-ui/src/components/mcp/McpEnabledToggle.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
import { FormEvent } from "react"
import { useExtensionState } from "../../context/ExtensionStateContext"
import { useAppTranslation } from "../../i18n/TranslationContext"
import { vscode } from "../../utils/vscode"

const McpEnabledToggle = () => {
const { mcpEnabled, setMcpEnabled } = useExtensionState()
const { t } = useAppTranslation()

const handleChange = (e: Event | FormEvent<HTMLElement>) => {
const target = ("target" in e ? e.target : null) as HTMLInputElement | null
Expand All @@ -16,16 +18,15 @@ const McpEnabledToggle = () => {
return (
<div style={{ marginBottom: "20px" }}>
<VSCodeCheckbox checked={mcpEnabled} onChange={handleChange}>
<span style={{ fontWeight: "500" }}>Enable MCP Servers</span>
<span style={{ fontWeight: "500" }}>{t("mcp:enableToggle.title")}</span>
</VSCodeCheckbox>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
When enabled, Roo will be able to interact with MCP servers for advanced functionality. If you're not
using MCP, you can disable this to reduce Roo's token usage.
{t("mcp:enableToggle.description")}
</p>
</div>
)
Expand Down
8 changes: 5 additions & 3 deletions webview-ui/src/components/mcp/McpToolRow.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
import { McpTool } from "../../../../src/shared/mcp"
import { useAppTranslation } from "../../i18n/TranslationContext"
import { vscode } from "../../utils/vscode"

type McpToolRowProps = {
Expand All @@ -9,6 +10,7 @@ type McpToolRowProps = {
}

const McpToolRow = ({ tool, serverName, alwaysAllowMcp }: McpToolRowProps) => {
const { t } = useAppTranslation()
const handleAlwaysAllowChange = () => {
if (!serverName) return

Expand Down Expand Up @@ -36,7 +38,7 @@ const McpToolRow = ({ tool, serverName, alwaysAllowMcp }: McpToolRowProps) => {
</div>
{serverName && alwaysAllowMcp && (
<VSCodeCheckbox checked={tool.alwaysAllow} onChange={handleAlwaysAllowChange} data-tool={tool.name}>
Always allow
{t("mcp:tool.alwaysAllow")}
</VSCodeCheckbox>
)}
</div>
Expand Down Expand Up @@ -64,7 +66,7 @@ const McpToolRow = ({ tool, serverName, alwaysAllowMcp }: McpToolRowProps) => {
}}>
<div
style={{ marginBottom: "4px", opacity: 0.8, fontSize: "11px", textTransform: "uppercase" }}>
Parameters
{t("mcp:tool.parameters")}
</div>
{Object.entries(tool.inputSchema.properties as Record<string, any>).map(
([paramName, schema]) => {
Expand Down Expand Up @@ -98,7 +100,7 @@ const McpToolRow = ({ tool, serverName, alwaysAllowMcp }: McpToolRowProps) => {
overflowWrap: "break-word",
wordBreak: "break-word",
}}>
{schema.description || "No description"}
{schema.description || t("mcp:tool.noDescription")}
</span>
</div>
)
Expand Down
80 changes: 42 additions & 38 deletions webview-ui/src/components/mcp/McpView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { vscode } from "@/utils/vscode"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui"

import { useExtensionState } from "../../context/ExtensionStateContext"
import { useAppTranslation } from "../../i18n/TranslationContext"
import { Trans } from "react-i18next"
import { Tab, TabContent, TabHeader } from "../common/Tab"
import McpToolRow from "./McpToolRow"
import McpResourceRow from "./McpResourceRow"
Expand All @@ -31,12 +33,13 @@ const McpView = ({ onDone }: McpViewProps) => {
enableMcpServerCreation,
setEnableMcpServerCreation,
} = useExtensionState()
const { t } = useAppTranslation()

return (
<Tab>
<TabHeader className="flex justify-between items-center">
<h3 className="text-vscode-foreground m-0">MCP Servers</h3>
<VSCodeButton onClick={onDone}>Done</VSCodeButton>
<h3 className="text-vscode-foreground m-0">{t("mcp:title")}</h3>
<VSCodeButton onClick={onDone}>{t("mcp:done")}</VSCodeButton>
</TabHeader>

<TabContent>
Expand All @@ -47,17 +50,16 @@ const McpView = ({ onDone }: McpViewProps) => {
marginBottom: "10px",
marginTop: "5px",
}}>
The{" "}
<VSCodeLink href="https://github.com/modelcontextprotocol" style={{ display: "inline" }}>
Model Context Protocol
</VSCodeLink>{" "}
enables communication with locally running MCP servers that provide additional tools and resources
to extend Roo's capabilities. You can use{" "}
<VSCodeLink href="https://github.com/modelcontextprotocol/servers" style={{ display: "inline" }}>
community-made servers
</VSCodeLink>{" "}
or ask Roo to create new tools specific to your workflow (e.g., "add a tool that gets the latest npm
docs").
<Trans i18nKey="mcp:description">
<VSCodeLink href="https://github.com/modelcontextprotocol" style={{ display: "inline" }}>
Model Context Protocol
</VSCodeLink>
<VSCodeLink
href="https://github.com/modelcontextprotocol/servers"
style={{ display: "inline" }}>
community-made servers
</VSCodeLink>
</Trans>
</div>

<McpEnabledToggle />
Expand All @@ -71,17 +73,15 @@ const McpView = ({ onDone }: McpViewProps) => {
setEnableMcpServerCreation(e.target.checked)
vscode.postMessage({ type: "enableMcpServerCreation", bool: e.target.checked })
}}>
<span style={{ fontWeight: "500" }}>Enable MCP Server Creation</span>
<span style={{ fontWeight: "500" }}>{t("mcp:enableServerCreation.title")}</span>
</VSCodeCheckbox>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
When enabled, Roo can help you create new MCP servers via commands like "add a new tool
to...". If you don't need to create MCP servers you can disable this to reduce Roo's
token usage.
{t("mcp:enableServerCreation.description")}
</p>
</div>

Expand All @@ -103,7 +103,7 @@ const McpView = ({ onDone }: McpViewProps) => {
vscode.postMessage({ type: "openMcpSettings" })
}}>
<span className="codicon codicon-edit" style={{ marginRight: "6px" }}></span>
Edit MCP Settings
{t("mcp:editSettings")}
</VSCodeButton>
</div>
</>
Expand All @@ -114,6 +114,7 @@ const McpView = ({ onDone }: McpViewProps) => {
}

const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowMcp?: boolean }) => {
const { t } = useAppTranslation()
const [isExpanded, setIsExpanded] = useState(false)
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)
const [timeoutValue, setTimeoutValue] = useState(() => {
Expand All @@ -122,14 +123,14 @@ const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowM
})

const timeoutOptions = [
{ value: 15, label: "15 seconds" },
{ value: 30, label: "30 seconds" },
{ value: 60, label: "1 minute" },
{ value: 300, label: "5 minutes" },
{ value: 600, label: "10 minutes" },
{ value: 900, label: "15 minutes" },
{ value: 1800, label: "30 minutes" },
{ value: 3600, label: "60 minutes" },
{ value: 15, label: t("mcp:networkTimeout.options.15seconds") },
{ value: 30, label: t("mcp:networkTimeout.options.30seconds") },
{ value: 60, label: t("mcp:networkTimeout.options.1minute") },
{ value: 300, label: t("mcp:networkTimeout.options.5minutes") },
{ value: 600, label: t("mcp:networkTimeout.options.10minutes") },
{ value: 900, label: t("mcp:networkTimeout.options.15minutes") },
{ value: 1800, label: t("mcp:networkTimeout.options.30minutes") },
{ value: 3600, label: t("mcp:networkTimeout.options.60minutes") },
]

const getStatusColor = () => {
Expand Down Expand Up @@ -291,7 +292,9 @@ const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowM
onClick={handleRestart}
disabled={server.status === "connecting"}
style={{ width: "calc(100% - 20px)", margin: "0 10px 10px 10px" }}>
{server.status === "connecting" ? "Retrying..." : "Retry Connection"}
{server.status === "connecting"
? t("mcp:serverStatus.retrying")
: t("mcp:serverStatus.retryConnection")}
</VSCodeButton>
</div>
) : (
Expand All @@ -304,9 +307,11 @@ const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowM
borderRadius: "0 0 4px 4px",
}}>
<VSCodePanels style={{ marginBottom: "10px" }}>
<VSCodePanelTab id="tools">Tools ({server.tools?.length || 0})</VSCodePanelTab>
<VSCodePanelTab id="tools">
{t("mcp:tabs.tools")} ({server.tools?.length || 0})
</VSCodePanelTab>
<VSCodePanelTab id="resources">
Resources (
{t("mcp:tabs.resources")} (
{[...(server.resourceTemplates || []), ...(server.resources || [])].length || 0})
</VSCodePanelTab>

Expand All @@ -325,7 +330,7 @@ const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowM
</div>
) : (
<div style={{ padding: "10px 0", color: "var(--vscode-descriptionForeground)" }}>
No tools found
{t("mcp:emptyState.noTools")}
</div>
)}
</VSCodePanelView>
Expand All @@ -346,7 +351,7 @@ const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowM
</div>
) : (
<div style={{ padding: "10px 0", color: "var(--vscode-descriptionForeground)" }}>
No resources found
{t("mcp:emptyState.noResources")}
</div>
)}
</VSCodePanelView>
Expand All @@ -361,7 +366,7 @@ const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowM
gap: "10px",
marginBottom: "8px",
}}>
<span>Network Timeout</span>
<span>{t("mcp:networkTimeout.label")}</span>
<select
value={timeoutValue}
onChange={handleTimeoutChange}
Expand All @@ -388,7 +393,7 @@ const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowM
color: "var(--vscode-descriptionForeground)",
display: "block",
}}>
Maximum time to wait for server responses
{t("mcp:networkTimeout.description")}
</span>
</div>
</div>
Expand All @@ -399,18 +404,17 @@ const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowM
<Dialog open={showDeleteConfirm} onOpenChange={setShowDeleteConfirm}>
<DialogContent>
<DialogHeader>
<DialogTitle>Delete MCP Server</DialogTitle>
<DialogTitle>{t("mcp:deleteDialog.title")}</DialogTitle>
<DialogDescription>
Are you sure you want to delete the MCP server "{server.name}"? This action cannot be
undone.
{t("mcp:deleteDialog.description", { serverName: server.name })}
</DialogDescription>
</DialogHeader>
<DialogFooter>
<VSCodeButton appearance="secondary" onClick={() => setShowDeleteConfirm(false)}>
Cancel
{t("mcp:deleteDialog.cancel")}
</VSCodeButton>
<VSCodeButton appearance="primary" onClick={handleDelete}>
Delete
{t("mcp:deleteDialog.delete")}
</VSCodeButton>
</DialogFooter>
</DialogContent>
Expand Down
14 changes: 14 additions & 0 deletions webview-ui/src/components/mcp/__tests__/McpToolRow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@ import { render, fireEvent, screen } from "@testing-library/react"
import McpToolRow from "../McpToolRow"
import { vscode } from "../../../utils/vscode"

// Mock the translation hook
jest.mock("../../../i18n/TranslationContext", () => ({
useAppTranslation: () => ({
t: (key: string) => {
const translations: Record<string, string> = {
"mcp:tool.alwaysAllow": "Always allow",
"mcp:tool.parameters": "Parameters",
"mcp:tool.noDescription": "No description",
}
return translations[key] || key
},
}),
}))

jest.mock("../../../utils/vscode", () => ({
vscode: {
postMessage: jest.fn(),
Expand Down
51 changes: 51 additions & 0 deletions webview-ui/src/i18n/locales/ar/mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"title": "خوادم MCP",
"done": "تم",
"description": "يتيح <0>بروتوكول سياق النموذج (Model Context Protocol)</0> الاتصال بخوادم MCP المحلية التي توفر أدوات وموارد إضافية لتوسيع قدرات Roo. يمكنك استخدام <1>الخوادم التي أنشأها المجتمع</1> أو مطالبة Roo بإنشاء أدوات جديدة مخصصة لسير عملك (مثل \"إضافة أداة تحصل على أحدث وثائق npm\").",
"enableToggle": {
"title": "تمكين خوادم MCP",
"description": "عند التمكين، سيتمكن Roo من التفاعل مع خوادم MCP للحصول على وظائف متقدمة. إذا كنت لا تستخدم MCP، يمكنك تعطيل هذا لتقليل استخدام token بواسطة Roo."
},
"enableServerCreation": {
"title": "تمكين إنشاء خادم MCP",
"description": "عند التمكين، يمكن لـ Roo مساعدتك في إنشاء خوادم MCP جديدة عبر أوامر مثل \"إضافة أداة جديدة إلى...\". إذا كنت لا تحتاج إلى إنشاء خوادم MCP، يمكنك تعطيل هذا لتقليل استخدام token بواسطة Roo."
},
"editSettings": "تعديل إعدادات MCP",
"tool": {
"alwaysAllow": "السماح دائمًا",
"parameters": "المعلمات",
"noDescription": "لا يوجد وصف"
},
"tabs": {
"tools": "الأدوات",
"resources": "الموارد"
},
"emptyState": {
"noTools": "لم يتم العثور على أدوات",
"noResources": "لم يتم العثور على موارد"
},
"networkTimeout": {
"label": "مهلة الشبكة",
"description": "الحد الأقصى لوقت الانتظار لاستجابات الخادم",
"options": {
"15seconds": "15 ثانية",
"30seconds": "30 ثانية",
"1minute": "1 دقيقة",
"5minutes": "5 دقائق",
"10minutes": "10 دقائق",
"15minutes": "15 دقيقة",
"30minutes": "30 دقيقة",
"60minutes": "60 دقيقة"
}
},
"deleteDialog": {
"title": "حذف خادم MCP",
"description": "هل أنت متأكد أنك تريد حذف خادم MCP \"{{serverName}}\"؟ لا يمكن التراجع عن هذا الإجراء.",
"cancel": "إلغاء",
"delete": "حذف"
},
"serverStatus": {
"retrying": "إعادة المحاولة...",
"retryConnection": "إعادة محاولة الاتصال"
}
}
51 changes: 51 additions & 0 deletions webview-ui/src/i18n/locales/ca/mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"title": "Servidors MCP",
"done": "Fet",
"description": "El <0>Model Context Protocol</0> permet la comunicació amb servidors MCP que s'executen localment i proporcionen eines i recursos addicionals per ampliar les capacitats de Roo. Pots utilitzar <1>servidors creats per la comunitat</1> o demanar a Roo que creï noves eines específiques per al teu flux de treball (per exemple, \"afegir una eina que obtingui la documentació més recent de npm\").",
"enableToggle": {
"title": "Habilitar servidors MCP",
"description": "Quan està habilitat, Roo podrà interactuar amb servidors MCP per a funcionalitats avançades. Si no utilitzes MCP, pots desactivar això per reduir l'ús de tokens de Roo."
},
"enableServerCreation": {
"title": "Habilitar creació de servidors MCP",
"description": "Quan està habilitat, Roo pot ajudar-te a crear nous servidors MCP mitjançant ordres com \"afegir una nova eina per a...\". Si no necessites crear servidors MCP, pots desactivar això per reduir l'ús de tokens de Roo."
},
"editSettings": "Editar configuració de MCP",
"tool": {
"alwaysAllow": "Permetre sempre",
"parameters": "Paràmetres",
"noDescription": "Sense descripció"
},
"tabs": {
"tools": "Eines",
"resources": "Recursos"
},
"emptyState": {
"noTools": "No s'han trobat eines",
"noResources": "No s'han trobat recursos"
},
"networkTimeout": {
"label": "Temps d'espera de xarxa",
"description": "Temps màxim d'espera per a respostes del servidor",
"options": {
"15seconds": "15 segons",
"30seconds": "30 segons",
"1minute": "1 minut",
"5minutes": "5 minuts",
"10minutes": "10 minuts",
"15minutes": "15 minuts",
"30minutes": "30 minuts",
"60minutes": "60 minuts"
}
},
"deleteDialog": {
"title": "Eliminar servidor MCP",
"description": "Estàs segur que vols eliminar el servidor MCP \"{{serverName}}\"? Aquesta acció no es pot desfer.",
"cancel": "Cancel·lar",
"delete": "Eliminar"
},
"serverStatus": {
"retrying": "Reintentant...",
"retryConnection": "Reintentar connexió"
}
}
Loading