Support specifying available and unavailable MCP servers in custom mode

This commit is contained in:
axb 2025-06-20 11:16:43 +08:00 committed by Daniel Riccio
parent 1237eb825b
commit d474779023
No known key found for this signature in database
GPG key ID: FFD5FD825F8E8209
23 changed files with 693 additions and 68 deletions

View file

@ -26,6 +26,23 @@ export const groupOptionsSchema = z.object({
{ message: "Invalid regular expression pattern" },
),
description: z.string().optional(),
mcp: z
.object({
included: z.array(
z.union([
z.string(),
z.record(
z.string(),
z.object({
allowedTools: z.array(z.string()).optional(), // not used yet
disallowedTools: z.array(z.string()).optional(), // not used yet
}),
),
]),
),
description: z.string().optional(),
})
.optional(),
})
export type GroupOptions = z.infer<typeof groupOptionsSchema>

View file

@ -1,20 +1,79 @@
import { DiffStrategy } from "../../../shared/tools"
import { McpHub } from "../../../services/mcp/McpHub"
import { GroupEntry, ModeConfig } from "@roo-code/types"
import { getGroupName } from "../../../shared/modes"
import { McpServer } from "../../../shared/mcp"
let lastMcpHub: McpHub | undefined
let lastMcpIncludedList: string[] | undefined
let lastFilteredServers: McpServer[] = []
function memoizeFilteredServers(mcpHub: McpHub, mcpIncludedList?: string[]): McpServer[] {
const mcpHubChanged = mcpHub !== lastMcpHub
const listChanged = !areArraysEqual(mcpIncludedList, lastMcpIncludedList)
if (!mcpHubChanged && !listChanged) {
return lastFilteredServers
}
lastMcpHub = mcpHub
lastMcpIncludedList = mcpIncludedList
lastFilteredServers = (
mcpIncludedList && mcpIncludedList.length > 0 ? mcpHub.getAllServers() : mcpHub.getServers()
).filter((server) => {
if (mcpIncludedList && mcpIncludedList.length > 0) {
return mcpIncludedList.includes(server.name) && server.status === "connected"
}
return server.status === "connected"
})
return lastFilteredServers
}
function areArraysEqual(arr1?: string[], arr2?: string[]): boolean {
if (!arr1 && !arr2) return true
if (!arr1 || !arr2) return false
if (arr1.length !== arr2.length) return false
return arr1.every((item, index) => item === arr2[index])
}
export async function getMcpServersSection(
mcpHub?: McpHub,
diffStrategy?: DiffStrategy,
enableMcpServerCreation?: boolean,
currentMode?: ModeConfig,
): Promise<string> {
if (!mcpHub) {
return ""
}
// Get MCP configuration for current mode
let mcpIncludedList: string[] | undefined
if (currentMode) {
// Find MCP group configuration
const mcpGroup = currentMode.groups.find((group: GroupEntry) => {
if (Array.isArray(group) && group.length === 2 && group[0] === "mcp") {
return true
}
return getGroupName(group) === "mcp"
})
// If MCP group configuration is found, get mcpIncludedList from mcp.included
if (mcpGroup && Array.isArray(mcpGroup) && mcpGroup.length === 2) {
const options = mcpGroup[1] as { mcp?: { included?: unknown[] } }
mcpIncludedList = Array.isArray(options.mcp?.included)
? options.mcp.included.filter((item: unknown): item is string => typeof item === "string")
: undefined
}
}
const filteredServers = memoizeFilteredServers(mcpHub, mcpIncludedList)
const connectedServers =
mcpHub.getServers().length > 0
? `${mcpHub
.getServers()
.filter((server) => server.status === "connected")
filteredServers.length > 0
? `${filteredServers
.map((server) => {
const tools = server.tools
?.filter((tool) => tool.enabledForPrompt !== false)

View file

@ -81,7 +81,7 @@ async function generatePrompt(
const [modesSection, mcpServersSection] = await Promise.all([
getModesSection(context),
shouldIncludeMcp
? getMcpServersSection(mcpHub, effectiveDiffStrategy, enableMcpServerCreation)
? getMcpServersSection(mcpHub, effectiveDiffStrategy, enableMcpServerCreation, modeConfig)
: Promise.resolve(""),
])

View file

@ -0,0 +1,256 @@
import React, { useState, useRef, useEffect } from "react"
import { ChevronsUpDown, X } from "lucide-react"
import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
import {
Popover,
PopoverContent,
PopoverTrigger,
Button,
Command,
CommandInput,
CommandList,
CommandEmpty,
CommandGroup,
CommandItem,
} from "@src/components/ui"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { ModeConfig, GroupEntry, GroupOptions } from "@roo-code/types"
import { McpServer } from "@roo/mcp"
interface McpSelectorProps {
group: string
isEnabled: boolean
isCustomMode: boolean
mcpServers: McpServer[]
currentMode?: ModeConfig
visualMode: string
customModes: ModeConfig[]
findModeBySlug: (slug: string, modes: ModeConfig[]) => ModeConfig | undefined
updateCustomMode: (slug: string, config: ModeConfig) => void
}
const McpSelector: React.FC<McpSelectorProps> = ({
group,
isEnabled,
isCustomMode,
mcpServers,
currentMode,
visualMode,
customModes,
findModeBySlug,
updateCustomMode,
}) => {
const { t } = useAppTranslation()
// State
const [isDialogOpen, setIsDialogOpen] = useState(false)
const [mcpIncludedList, setMcpIncludedList] = useState<string[]>([])
const [searchValue, setSearchValue] = useState("")
const searchInputRef = useRef<HTMLInputElement | null>(null)
// Sync MCP settings
useEffect(() => {
if (!currentMode) {
setMcpIncludedList([])
return
}
const mcpGroupArr = currentMode.groups?.find(
(g: GroupEntry): g is ["mcp", GroupOptions] => Array.isArray(g) && g.length === 2 && g[0] === "mcp",
)
const rawGroupOptions: GroupOptions | undefined = mcpGroupArr ? mcpGroupArr[1] : undefined
const included = Array.isArray(rawGroupOptions?.mcp?.included)
? (rawGroupOptions.mcp.included.filter((item) => typeof item === "string") as string[])
: []
// Sync MCP settings when mode changes
setMcpIncludedList(included)
}, [currentMode])
// Handle save
function updateMcpGroupOptions(groups: GroupEntry[] = [], group: string, mcpIncludedList: string[]): GroupEntry[] {
let mcpGroupFound = false
const newGroups = groups
.map((g) => {
if (Array.isArray(g) && g[0] === group) {
mcpGroupFound = true
return [
group,
{
...(g[1] || {}),
mcp: mcpIncludedList.length > 0 ? { included: mcpIncludedList } : undefined,
},
] as GroupEntry
}
if (typeof g === "string" && g === group) {
mcpGroupFound = true
return [
group,
{
mcp: mcpIncludedList.length > 0 ? { included: mcpIncludedList } : undefined,
},
] as GroupEntry
}
return g
})
.filter((g) => g !== undefined)
if (!mcpGroupFound && group === "mcp") {
const groupsWithoutSimpleMcp = newGroups.filter((g) => g !== "mcp")
groupsWithoutSimpleMcp.push([
"mcp",
{
mcp: mcpIncludedList.length > 0 ? { included: mcpIncludedList } : undefined,
},
])
return groupsWithoutSimpleMcp as GroupEntry[]
} else {
return newGroups as GroupEntry[]
}
}
// Handle save
const handleSave = () => {
const customMode = findModeBySlug(visualMode, customModes)
if (!customMode) {
setIsDialogOpen(false)
return
}
const updatedGroups = updateMcpGroupOptions(customMode.groups, group, mcpIncludedList)
updateCustomMode(customMode.slug, {
...customMode,
groups: updatedGroups,
source: customMode.source || "global",
})
setIsDialogOpen(false)
}
if (!isCustomMode || !isEnabled) {
return null
}
return (
<Popover
open={isDialogOpen}
onOpenChange={(open) => {
setIsDialogOpen(open)
// Reset search box
if (!open) {
setTimeout(() => {
setSearchValue("")
}, 100)
}
}}>
<PopoverTrigger asChild>
<Button variant="secondary" size="sm" style={{ marginLeft: 4 }} className="flex items-center gap-1">
{/* Dynamically display button text */}
{mcpIncludedList.length === 0
? t("prompts:tools.mcpAll")
: t("prompts:tools.mcpSelectedCount", {
included: mcpIncludedList.length,
})}
<ChevronsUpDown className="opacity-50 size-3" />
</Button>
</PopoverTrigger>
<PopoverContent className="p-0 w-[400px] bg-vscode-editor-background">
<Command>
<div className="flex items-center border-b border-vscode-input-border p-2">
<div className="font-medium text-sm flex-1">{t("prompts:tools.selectMcpServers")}</div>
<div className="flex gap-2">
<Button variant="secondary" size="sm" onClick={() => setMcpIncludedList([])}>
{t("prompts:tools.buttons.clearAll")}
</Button>
<Button variant="default" size="sm" onClick={handleSave}>
{t("prompts:tools.buttons.save")}
</Button>
</div>
</div>
<div className="relative">
<CommandInput
ref={searchInputRef}
value={searchValue}
onValueChange={setSearchValue}
placeholder={t("prompts:tools.searchMcpServers")}
className="h-9 mr-4"
/>
{searchValue.length > 0 && (
<div className="absolute right-2 top-0 bottom-0 flex items-center justify-center">
<X
className="text-vscode-input-foreground opacity-50 hover:opacity-100 size-4 p-0.5 cursor-pointer"
onClick={() => {
setSearchValue("")
searchInputRef.current?.focus()
}}
/>
</div>
)}
</div>
<div className="border-b border-vscode-input-border p-2">
<div className="text-sm font-medium text-vscode-foreground mb-2">
{t("prompts:tools.requiredMcpList")}
</div>
<div className="text-sm text-vscode-descriptionForeground mb-2">
{t("prompts:tools.mcpDefaultDescription")}
</div>
<CommandList className="max-h-[150px] overflow-auto bg-vscode-editorWidget-background">
<CommandEmpty>
{mcpServers.length === 0 ? (
<div className="py-2 px-2 text-sm text-vscode-descriptionForeground">
{t("prompts:tools.noMcpServers")}
</div>
) : (
<div className="py-2 px-2 text-sm">{t("prompts:tools.noMatchFound")}</div>
)}
</CommandEmpty>
<CommandGroup>
{mcpServers
.filter(
(server) =>
!searchValue ||
server.name.toLowerCase().includes(searchValue.toLowerCase()),
)
.map((server) => (
<CommandItem
key={`included-${server.name}`}
value={`included-${server.name}`}
onSelect={() => {
const isIncluded = mcpIncludedList.includes(server.name)
if (isIncluded) {
setMcpIncludedList(mcpIncludedList.filter((n) => n !== server.name))
} else {
setMcpIncludedList([...mcpIncludedList, server.name])
}
}}
className="flex items-center px-2 py-1">
<div className="flex items-center flex-1 gap-2">
<VSCodeCheckbox
checked={mcpIncludedList.includes(server.name)}
onClick={(e) => {
e.stopPropagation()
const isIncluded = mcpIncludedList.includes(server.name)
if (isIncluded) {
setMcpIncludedList(
mcpIncludedList.filter((n) => n !== server.name),
)
} else {
setMcpIncludedList([...mcpIncludedList, server.name])
}
}}
/>
<span>{server.name}</span>
</div>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</div>
</Command>
</PopoverContent>
</Popover>
)
}
export default McpSelector

View file

@ -49,6 +49,7 @@ import {
} from "@src/components/ui"
import { DeleteModeDialog } from "@src/components/modes/DeleteModeDialog"
import { useEscapeKey } from "@src/hooks/useEscapeKey"
import McpSelector from "./McpSelector"
// Get all available groups that should show in prompts view
const availableGroups = (Object.keys(TOOL_GROUPS) as ToolGroup[]).filter((group) => !TOOL_GROUPS[group].alwaysAvailable)
@ -75,6 +76,7 @@ const ModesView = ({ onDone }: ModesViewProps) => {
customInstructions,
setCustomInstructions,
customModes,
mcpServers,
} = useExtensionState()
// Use a local state to track the visually active mode
@ -1018,33 +1020,76 @@ const ModesView = ({ onDone }: ModesViewProps) => {
? customMode?.groups?.some((g) => getGroupName(g) === group)
: currentMode?.groups?.some((g) => getGroupName(g) === group)
const isMcpGroup = group === "mcp"
if (isMcpGroup) {
return (
<div
key={group}
className="col-span-2"
style={{ display: "flex", alignItems: "center", gap: 8 }}>
<VSCodeCheckbox
key={group}
checked={isGroupEnabled}
onChange={handleGroupChange(
group,
Boolean(isCustomMode),
customMode,
)}
disabled={!isCustomMode}>
{t(`prompts:tools.toolNames.${group}`)}
</VSCodeCheckbox>
{isGroupEnabled && isCustomMode && (
<McpSelector
group={group}
isEnabled={isGroupEnabled}
isCustomMode={Boolean(isCustomMode)}
mcpServers={mcpServers}
currentMode={currentMode}
visualMode={visualMode}
customModes={customModes}
findModeBySlug={findModeBySlug}
updateCustomMode={updateCustomMode}
/>
)}
</div>
)
}
return (
<VSCodeCheckbox
key={group}
checked={isGroupEnabled}
onChange={handleGroupChange(group, Boolean(isCustomMode), customMode)}
disabled={!isCustomMode}>
{t(`prompts:tools.toolNames.${group}`)}
{group === "edit" && (
<div className="text-xs text-vscode-descriptionForeground mt-0.5">
{t("prompts:tools.allowedFiles")}{" "}
{(() => {
const currentMode = getCurrentMode()
const editGroup = currentMode?.groups?.find(
(g) =>
Array.isArray(g) &&
g[0] === "edit" &&
g[1]?.fileRegex,
)
if (!Array.isArray(editGroup)) return t("prompts:allFiles")
return (
editGroup[1].description ||
`/${editGroup[1].fileRegex}/`
)
})()}
</div>
)}
</VSCodeCheckbox>
<div key={group} style={{ display: "flex", alignItems: "center", gap: 8 }}>
<VSCodeCheckbox
key={group}
checked={isGroupEnabled}
onChange={handleGroupChange(
group,
Boolean(isCustomMode),
customMode,
)}
disabled={!isCustomMode}>
{t(`prompts:tools.toolNames.${group}`)}
{group === "edit" && (
<div className="text-xs text-vscode-descriptionForeground mt-0.5">
{t("prompts:tools.allowedFiles")}{" "}
{(() => {
const currentMode = getCurrentMode()
const editGroup = currentMode?.groups?.find(
(g) =>
Array.isArray(g) &&
g[0] === "edit" &&
g[1]?.fileRegex,
)
if (!Array.isArray(editGroup))
return t("prompts:allFiles")
return (
editGroup[1].description ||
`/${editGroup[1].fileRegex}/`
)
})()}
</div>
)}
</VSCodeCheckbox>
</div>
)
})}
</div>
@ -1494,25 +1539,57 @@ const ModesView = ({ onDone }: ModesViewProps) => {
{t("prompts:createModeDialog.tools.description")}
</div>
<div className="grid grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-2">
{availableGroups.map((group) => (
<VSCodeCheckbox
key={group}
checked={newModeGroups.some((g) => getGroupName(g) === group)}
onChange={(e: Event | React.FormEvent<HTMLElement>) => {
const target =
(e as CustomEvent)?.detail?.target || (e.target as HTMLInputElement)
const checked = target.checked
if (checked) {
setNewModeGroups([...newModeGroups, group])
} else {
setNewModeGroups(
newModeGroups.filter((g) => getGroupName(g) !== group),
)
}
}}>
{t(`prompts:tools.toolNames.${group}`)}
</VSCodeCheckbox>
))}
{availableGroups.map((group) => {
if (group === "mcp") {
return (
<div
key={group}
className="col-span-2"
style={{ display: "flex", alignItems: "center", gap: 8 }}>
<VSCodeCheckbox
key={group}
checked={newModeGroups.some((g) => getGroupName(g) === group)}
onChange={(e: Event | React.FormEvent<HTMLElement>) => {
const target =
(e as CustomEvent)?.detail?.target ||
(e.target as HTMLInputElement)
const checked = target.checked
if (checked) {
setNewModeGroups([...newModeGroups, group])
} else {
setNewModeGroups(
newModeGroups.filter(
(g) => getGroupName(g) !== group,
),
)
}
}}>
{t(`prompts:tools.toolNames.${group}`)}
</VSCodeCheckbox>
</div>
)
}
return (
<VSCodeCheckbox
key={group}
checked={newModeGroups.some((g) => getGroupName(g) === group)}
onChange={(e: Event | React.FormEvent<HTMLElement>) => {
const target =
(e as CustomEvent)?.detail?.target ||
(e.target as HTMLInputElement)
const checked = target.checked
if (checked) {
setNewModeGroups([...newModeGroups, group])
} else {
setNewModeGroups(
newModeGroups.filter((g) => getGroupName(g) !== group),
)
}
}}>
{t(`prompts:tools.toolNames.${group}`)}
</VSCodeCheckbox>
)
})}
</div>
{groupsError && (
<div className="text-xs text-vscode-errorForeground mt-1">{groupsError}</div>

View file

@ -29,7 +29,19 @@
"command": "Executar comandes",
"mcp": "Utilitzar MCP"
},
"noTools": "Cap"
"selectMcpServers": "Selecciona servidors MCP",
"searchMcpServers": "Cerca servidors MCP",
"requiredMcpList": "Servidors MCP requerits",
"noMcpServers": "No s'han trobat servidors MCP",
"noMatchFound": "No s'ha trobat cap coincidència",
"noTools": "Cap",
"mcpAll": "Tots",
"mcpSelectedCount": "{{included}} inclosos",
"mcpDefaultDescription": "Si no se selecciona cap servidor MCP, s'activaran tots per defecte.",
"buttons": {
"save": "Desa",
"clearAll": "Neteja-ho tot"
}
},
"roleDefinition": {
"title": "Definició de rol",

View file

@ -29,7 +29,19 @@
"command": "Befehle ausführen",
"mcp": "MCP verwenden"
},
"noTools": "Keine"
"selectMcpServers": "MCP-Server auswählen",
"searchMcpServers": "MCP-Server suchen",
"requiredMcpList": "Erforderliche MCP-Server",
"noMcpServers": "Keine MCP-Server gefunden",
"noMatchFound": "Keine Übereinstimmung gefunden",
"noTools": "Keine",
"mcpAll": "Alle",
"mcpSelectedCount": "{{included}} enthalten",
"mcpDefaultDescription": "Wenn kein MCP-Server ausgewählt ist, werden standardmäßig alle aktiviert.",
"buttons": {
"save": "Speichern",
"clearAll": "Alles löschen"
}
},
"roleDefinition": {
"title": "Rollendefinition",

View file

@ -29,7 +29,19 @@
"command": "Run Commands",
"mcp": "Use MCP"
},
"noTools": "None"
"selectMcpServers": "Select MCP Servers",
"searchMcpServers": "Search MCP Servers",
"requiredMcpList": "Required MCP Servers",
"noMcpServers": "No MCP servers found",
"noMatchFound": "No match found",
"noTools": "None",
"mcpAll": "All",
"mcpSelectedCount": "{{included}} Included",
"mcpDefaultDescription": "If no MCP server is selected, all will be enabled by default.",
"buttons": {
"save": "Save",
"clearAll": "Clear All"
}
},
"roleDefinition": {
"title": "Role Definition",

View file

@ -29,7 +29,19 @@
"command": "Ejecutar comandos",
"mcp": "Usar MCP"
},
"noTools": "Ninguna"
"selectMcpServers": "Seleccionar servidores MCP",
"searchMcpServers": "Buscar servidores MCP",
"requiredMcpList": "Servidores MCP requeridos",
"noMcpServers": "No se encontraron servidores MCP",
"noMatchFound": "No se encontraron coincidencias",
"noTools": "Ninguna",
"mcpAll": "Todos",
"mcpSelectedCount": "{{included}} incluidos",
"mcpDefaultDescription": "Si no se selecciona ningún servidor MCP, se habilitarán todos por defecto.",
"buttons": {
"save": "Guardar",
"clearAll": "Limpiar todo"
}
},
"roleDefinition": {
"title": "Definición de rol",

View file

@ -29,7 +29,19 @@
"command": "Exécuter des commandes",
"mcp": "Utiliser MCP"
},
"noTools": "Aucun"
"selectMcpServers": "Sélectionner les serveurs MCP",
"searchMcpServers": "Rechercher les serveurs MCP",
"requiredMcpList": "Serveurs MCP requis",
"noMcpServers": "Aucun serveur MCP trouvé",
"noMatchFound": "Aucune correspondance trouvée",
"noTools": "Aucun",
"mcpAll": "Tous",
"mcpSelectedCount": "{{included}} inclus",
"mcpDefaultDescription": "Si aucun serveur MCP n'est sélectionné, tous seront activés par défaut.",
"buttons": {
"save": "Enregistrer",
"clearAll": "Tout effacer"
}
},
"roleDefinition": {
"title": "Définition du rôle",

View file

@ -29,7 +29,19 @@
"command": "कमांड्स चलाएँ",
"mcp": "MCP का उपयोग करें"
},
"noTools": "कोई नहीं"
"selectMcpServers": "MCP सर्वर चुनें",
"searchMcpServers": "MCP सर्वर खोजें",
"requiredMcpList": "आवश्यक MCP सर्वर",
"noMcpServers": "कोई MCP सर्वर नहीं मिला",
"noMatchFound": "कोई मिलान नहीं मिला",
"noTools": "कोई नहीं",
"mcpAll": "सभी",
"mcpSelectedCount": "{{included}} शामिल",
"mcpDefaultDescription": "यदि कोई MCP सर्वर चयनित नहीं है, तो सभी डिफ़ॉल्ट रूप से सक्षम होंगे।",
"buttons": {
"save": "सहेजें",
"clearAll": "सभी साफ़ करें"
}
},
"roleDefinition": {
"title": "भूमिका परिभाषा",

View file

@ -29,7 +29,19 @@
"command": "Jalankan Perintah",
"mcp": "Gunakan MCP"
},
"noTools": "Tidak Ada"
"selectMcpServers": "Pilih server MCP",
"searchMcpServers": "Cari server MCP",
"requiredMcpList": "Server MCP yang Diperlukan",
"noMcpServers": "Tidak ada server MCP ditemukan",
"noMatchFound": "Tidak ada hasil cocok",
"noTools": "Tidak Ada",
"mcpAll": "Semua",
"mcpSelectedCount": "{{included}} termasuk",
"mcpDefaultDescription": "Jika tidak ada server MCP yang dipilih, semua akan diaktifkan secara default.",
"buttons": {
"save": "Simpan",
"clearAll": "Bersihkan semua"
}
},
"roleDefinition": {
"title": "Definisi Peran",

View file

@ -29,7 +29,19 @@
"command": "Esegui comandi",
"mcp": "Usa MCP"
},
"noTools": "Nessuno"
"selectMcpServers": "Seleziona server MCP",
"searchMcpServers": "Cerca server MCP",
"requiredMcpList": "Server MCP richiesti",
"noMcpServers": "Nessun server MCP trovato",
"noMatchFound": "Nessuna corrispondenza trovata",
"noTools": "Nessuno",
"mcpAll": "Tutti",
"mcpSelectedCount": "{{included}} inclusi",
"mcpDefaultDescription": "Se non viene selezionato alcun server MCP, tutti saranno abilitati per impostazione predefinita.",
"buttons": {
"save": "Salva",
"clearAll": "Cancella tutto"
}
},
"roleDefinition": {
"title": "Definizione del ruolo",

View file

@ -29,7 +29,19 @@
"command": "コマンドを実行",
"mcp": "MCP を使用"
},
"noTools": "なし"
"selectMcpServers": "MCPサーバーを選択",
"searchMcpServers": "MCPサーバーを検索",
"requiredMcpList": "必要な MCP サーバー",
"noMcpServers": "MCPサーバーが見つかりません",
"noMatchFound": "一致するものがありません",
"noTools": "なし",
"mcpAll": "すべて",
"mcpSelectedCount": "{{included}} 含まれる",
"mcpDefaultDescription": "MCP サーバーが選択されていない場合、すべてがデフォルトで有効になります。",
"buttons": {
"save": "保存",
"clearAll": "すべてクリア"
}
},
"roleDefinition": {
"title": "役割の定義",

View file

@ -29,7 +29,19 @@
"command": "명령 실행",
"mcp": "MCP 사용"
},
"noTools": "없음"
"selectMcpServers": "MCP 서버 선택",
"searchMcpServers": "MCP 서버 검색",
"requiredMcpList": "필수 MCP 서버",
"noMcpServers": "MCP 서버를 찾을 수 없음",
"noMatchFound": "일치하는 항목 없음",
"noTools": "없음",
"mcpAll": "전체",
"mcpSelectedCount": "{{included}} 포함됨",
"mcpDefaultDescription": "MCP 서버를 선택하지 않으면 전체가 기본적으로 활성화됩니다.",
"buttons": {
"save": "저장",
"clearAll": "전체 해제"
}
},
"roleDefinition": {
"title": "역할 정의",

View file

@ -29,7 +29,19 @@
"command": "Commando's uitvoeren",
"mcp": "MCP gebruiken"
},
"noTools": "Geen"
"selectMcpServers": "Selecteer MCP-servers",
"searchMcpServers": "Zoek MCP-servers",
"requiredMcpList": "Vereiste MCP-servers",
"noMcpServers": "Geen MCP-servers gevonden",
"noMatchFound": "Geen overeenkomsten gevonden",
"noTools": "Geen",
"mcpAll": "Alle",
"mcpSelectedCount": "{{included}} inbegrepen",
"mcpDefaultDescription": "Als er geen MCP-server is geselecteerd, worden standaard alle ingeschakeld.",
"buttons": {
"save": "Opslaan",
"clearAll": "Alles wissen"
}
},
"roleDefinition": {
"title": "Roldefinitie",

View file

@ -29,7 +29,19 @@
"command": "Uruchamiaj polecenia",
"mcp": "Używaj MCP"
},
"noTools": "Brak"
"selectMcpServers": "Wybierz serwery MCP",
"searchMcpServers": "Szukaj serwerów MCP",
"requiredMcpList": "Wymagane serwery MCP",
"noMcpServers": "Nie znaleziono serwerów MCP",
"noMatchFound": "Brak pasujących wyników",
"noTools": "Brak",
"mcpAll": "Wszystkie",
"mcpSelectedCount": "{{included}} uwzględnione",
"mcpDefaultDescription": "Jeśli nie wybrano żadnego serwera MCP, domyślnie wszystkie będą włączone.",
"buttons": {
"save": "Zapisz",
"clearAll": "Wyczyść wszystko"
}
},
"roleDefinition": {
"title": "Definicja roli",

View file

@ -29,7 +29,19 @@
"command": "Executar comandos",
"mcp": "Usar MCP"
},
"noTools": "Nenhuma"
"selectMcpServers": "Selecionar servidores MCP",
"searchMcpServers": "Buscar servidores MCP",
"requiredMcpList": "Servidores MCP necessários",
"noMcpServers": "Nenhum servidor MCP encontrado",
"noMatchFound": "Nenhuma correspondência encontrada",
"noTools": "Nenhuma",
"mcpAll": "Todos",
"mcpSelectedCount": "{{included}} incluídos",
"mcpDefaultDescription": "Se nenhum servidor MCP for selecionado, todos serão ativados por padrão.",
"buttons": {
"save": "Salvar",
"clearAll": "Limpar tudo"
}
},
"roleDefinition": {
"title": "Definição de função",

View file

@ -29,7 +29,19 @@
"command": "Выполнять команды",
"mcp": "Использовать MCP"
},
"noTools": "Отсутствуют"
"selectMcpServers": "Выбери MCP-серверы",
"searchMcpServers": "Искать MCP-серверы",
"requiredMcpList": "Требуемые MCP-серверы",
"noMcpServers": "MCP-серверы не найдены",
"noMatchFound": "Совпадений не найдено",
"noTools": "Отсутствуют",
"mcpAll": "Все",
"mcpSelectedCount": "{{included}} включено",
"mcpDefaultDescription": "Если не выбран ни один MCP-сервер, по умолчанию будут активированы все.",
"buttons": {
"save": "Сохранить",
"clearAll": "Очистить всё"
}
},
"roleDefinition": {
"title": "Определение роли",

View file

@ -29,7 +29,19 @@
"command": "Komutları Çalıştır",
"mcp": "MCP Kullan"
},
"noTools": "Yok"
"selectMcpServers": "MCP sunucularını seç",
"searchMcpServers": "MCP sunucularını ara",
"requiredMcpList": "Gerekli MCP Sunucuları",
"noMcpServers": "MCP sunucusu bulunamadı",
"noMatchFound": "Eşleşme bulunamadı",
"noTools": "Yok",
"mcpAll": "Tümü",
"mcpSelectedCount": "{{included}} dahil",
"mcpDefaultDescription": "Hiçbir MCP sunucusu seçilmezse, varsayılan olarak tümü etkinleştirilir.",
"buttons": {
"save": "Kaydet",
"clearAll": "Tümünü temizle"
}
},
"roleDefinition": {
"title": "Rol Tanımı",

View file

@ -29,7 +29,19 @@
"command": "Chạy lệnh",
"mcp": "Sử dụng MCP"
},
"noTools": "Không có"
"selectMcpServers": "Chọn máy chủ MCP",
"searchMcpServers": "Tìm kiếm máy chủ MCP",
"requiredMcpList": "Máy chủ MCP cần thiết",
"noMcpServers": "Không tìm thấy máy chủ MCP",
"noMatchFound": "Không tìm thấy kết quả phù hợp",
"noTools": "Không có",
"mcpAll": "Tất cả",
"mcpSelectedCount": "{{included}} đã bao gồm",
"mcpDefaultDescription": "Nếu không chọn máy chủ MCP nào, tất cả sẽ được bật mặc định.",
"buttons": {
"save": "Lưu",
"clearAll": "Xóa tất cả"
}
},
"roleDefinition": {
"title": "Định nghĩa vai trò",

View file

@ -29,7 +29,19 @@
"command": "运行命令",
"mcp": "MCP服务"
},
"noTools": "无"
"selectMcpServers": "选择 MCP 服务",
"searchMcpServers": "搜索 MCP 服务",
"requiredMcpList": "所需 MCP 服务",
"noMcpServers": "未发现 MCP 服务",
"noMatchFound": "无匹配结果",
"noTools": "无",
"mcpAll": "全部",
"mcpSelectedCount": "{{included}} 已包含",
"mcpDefaultDescription": "如果未选择任何 MCP 服务,将默认启用全部。",
"buttons": {
"save": "保存",
"clearAll": "清除全部"
}
},
"roleDefinition": {
"title": "角色定义",

View file

@ -29,7 +29,19 @@
"command": "執行命令",
"mcp": "使用 MCP"
},
"noTools": "無"
"selectMcpServers": "選擇 MCP 伺服器",
"searchMcpServers": "搜尋 MCP 伺服器",
"requiredMcpList": "所需 MCP 伺服器",
"noMcpServers": "未找到 MCP 伺服器",
"noMatchFound": "沒有符合的結果",
"noTools": "無",
"mcpAll": "全部",
"mcpSelectedCount": "{{included}} 已包含",
"mcpDefaultDescription": "若未選擇任何 MCP 伺服器,則預設啟用全部。",
"buttons": {
"save": "儲存",
"clearAll": "清除全部"
}
},
"roleDefinition": {
"title": "角色定義",