From 9fd6e6f6229f567bc89353622aa405b26c848e85 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Fri, 16 Jan 2026 09:30:49 +0000 Subject: [PATCH 1/9] feat: warn users when too many MCP tools are enabled - Add WarningRow component for displaying generic warnings with icon, title, message, and optional docs link - Add TooManyToolsWarning component that shows when users have more than 40 MCP tools enabled - Add MAX_MCP_TOOLS_THRESHOLD constant (40) - Add i18n translations for the warning message - Integrate warning into ChatView to display after task header - Add comprehensive tests for both components Closes ROO-542 --- webview-ui/src/components/chat/ChatView.tsx | 5 + .../components/chat/TooManyToolsWarning.tsx | 67 +++++ webview-ui/src/components/chat/WarningRow.tsx | 57 ++++ .../__tests__/TooManyToolsWarning.spec.tsx | 253 ++++++++++++++++++ .../chat/__tests__/WarningRow.spec.tsx | 71 +++++ webview-ui/src/i18n/locales/en/chat.json | 6 + 6 files changed, 459 insertions(+) create mode 100644 webview-ui/src/components/chat/TooManyToolsWarning.tsx create mode 100644 webview-ui/src/components/chat/WarningRow.tsx create mode 100644 webview-ui/src/components/chat/__tests__/TooManyToolsWarning.spec.tsx create mode 100644 webview-ui/src/components/chat/__tests__/WarningRow.spec.tsx diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 81f6cbebf66..e8d1331784e 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -44,6 +44,7 @@ import TaskHeader from "./TaskHeader" import SystemPromptWarning from "./SystemPromptWarning" import ProfileViolationWarning from "./ProfileViolationWarning" import { CheckpointWarning } from "./CheckpointWarning" +import TooManyToolsWarning from "./TooManyToolsWarning" import { QueuedMessages } from "./QueuedMessages" import DismissibleUpsell from "../common/DismissibleUpsell" import { useCloudUpsell } from "@src/hooks/useCloudUpsell" @@ -1507,6 +1508,10 @@ const ChatViewComponent: React.ForwardRefRenderFunction )} + +
+ +
) : (
diff --git a/webview-ui/src/components/chat/TooManyToolsWarning.tsx b/webview-ui/src/components/chat/TooManyToolsWarning.tsx new file mode 100644 index 00000000000..55978ddd4bb --- /dev/null +++ b/webview-ui/src/components/chat/TooManyToolsWarning.tsx @@ -0,0 +1,67 @@ +import React, { useMemo } from "react" +import { useAppTranslation } from "@/i18n/TranslationContext" +import { useExtensionState } from "@src/context/ExtensionStateContext" +import WarningRow from "./WarningRow" + +/** + * The maximum number of MCP tools recommended before warning the user. + * Having too many tools can confuse LLMs and lead to errors. + */ +export const MAX_MCP_TOOLS_THRESHOLD = 40 + +/** + * Displays a warning when the user has too many MCP tools enabled. + * LLMs get confused when offered too many tools, which can lead to errors. + * + * The warning is shown when: + * - The total number of enabled tools across all enabled MCP servers exceeds the threshold + * + * @example + * + */ +export const TooManyToolsWarning: React.FC = () => { + const { t } = useAppTranslation() + const { mcpServers } = useExtensionState() + + const { enabledServerCount, enabledToolCount } = useMemo(() => { + let serverCount = 0 + let toolCount = 0 + + for (const server of mcpServers) { + // Skip disabled servers + if (server.disabled) continue + + // Skip servers that are not connected + if (server.status !== "connected") continue + + serverCount++ + + // Count enabled tools on this server + if (server.tools) { + for (const tool of server.tools) { + // Tool is enabled if enabledForPrompt is undefined (default) or true + if (tool.enabledForPrompt !== false) { + toolCount++ + } + } + } + } + + return { enabledServerCount: serverCount, enabledToolCount: toolCount } + }, [mcpServers]) + + // Don't show warning if under threshold + if (enabledToolCount <= MAX_MCP_TOOLS_THRESHOLD) { + return null + } + + const message = t("chat:tooManyTools.message", { + toolCount: enabledToolCount, + serverCount: enabledServerCount, + threshold: MAX_MCP_TOOLS_THRESHOLD, + }) + + return +} + +export default TooManyToolsWarning diff --git a/webview-ui/src/components/chat/WarningRow.tsx b/webview-ui/src/components/chat/WarningRow.tsx new file mode 100644 index 00000000000..87300436885 --- /dev/null +++ b/webview-ui/src/components/chat/WarningRow.tsx @@ -0,0 +1,57 @@ +import React from "react" +import { TriangleAlert, BookOpenText } from "lucide-react" +import { useAppTranslation } from "@/i18n/TranslationContext" +import { vscode } from "@src/utils/vscode" + +export interface WarningRowProps { + title: string + message: string + docsURL?: string +} + +/** + * A generic warning row component that displays a warning icon, title, and message. + * Optionally includes a documentation link. + * + * @param title - The warning title displayed in bold + * @param message - The warning message displayed below the title + * @param docsURL - Optional documentation link URL (shown as "Learn more" with book icon) + * + * @example + * + */ +export const WarningRow: React.FC = ({ title, message, docsURL }) => { + const { t } = useAppTranslation() + + return ( + + ) +} + +export default WarningRow diff --git a/webview-ui/src/components/chat/__tests__/TooManyToolsWarning.spec.tsx b/webview-ui/src/components/chat/__tests__/TooManyToolsWarning.spec.tsx new file mode 100644 index 00000000000..b000aec7e6e --- /dev/null +++ b/webview-ui/src/components/chat/__tests__/TooManyToolsWarning.spec.tsx @@ -0,0 +1,253 @@ +import { render, screen } from "@/utils/test-utils" + +import { TooManyToolsWarning, MAX_MCP_TOOLS_THRESHOLD } from "../TooManyToolsWarning" + +// Mock vscode webview messaging +vi.mock("@/utils/vscode", () => ({ + vscode: { + postMessage: vi.fn(), + }, +})) + +// Mock ExtensionState context with variable mcpServers +const mockMcpServers = vi.fn() + +vi.mock("@/context/ExtensionStateContext", () => ({ + useExtensionState: () => ({ + mcpServers: mockMcpServers(), + }), +})) + +// Mock i18n TranslationContext +vi.mock("@/i18n/TranslationContext", () => ({ + useAppTranslation: () => ({ + t: (key: string, params?: Record) => { + if (key === "chat:tooManyTools.title") { + return "Too many tools enabled" + } + if (key === "chat:tooManyTools.message") { + return `You have ${params?.toolCount} tools enabled via ${params?.serverCount} MCP servers. Such a high number can confuse the model and lead to errors. Try to keep it below ${params?.threshold}.` + } + if (key === "chat:apiRequest.errorMessage.docs") { + return "Docs" + } + return key + }, + }), +})) + +describe("TooManyToolsWarning", () => { + beforeEach(() => { + vi.clearAllMocks() + mockMcpServers.mockReturnValue([]) + }) + + it("does not render when there are no MCP servers", () => { + mockMcpServers.mockReturnValue([]) + + const { container } = render() + + expect(container.firstChild).toBeNull() + }) + + it("does not render when tool count is below threshold", () => { + mockMcpServers.mockReturnValue([ + { + name: "server1", + status: "connected", + disabled: false, + tools: [ + { name: "tool1", enabledForPrompt: true }, + { name: "tool2", enabledForPrompt: true }, + ], + }, + ]) + + const { container } = render() + + expect(container.firstChild).toBeNull() + }) + + it("does not render when tool count equals threshold", () => { + // Create tools to exactly match threshold + const tools = Array.from({ length: MAX_MCP_TOOLS_THRESHOLD }, (_, i) => ({ + name: `tool${i}`, + enabledForPrompt: true, + })) + + mockMcpServers.mockReturnValue([ + { + name: "server1", + status: "connected", + disabled: false, + tools, + }, + ]) + + const { container } = render() + + expect(container.firstChild).toBeNull() + }) + + it("renders warning when tool count exceeds threshold", () => { + // Create more tools than the threshold + const tools = Array.from({ length: MAX_MCP_TOOLS_THRESHOLD + 10 }, (_, i) => ({ + name: `tool${i}`, + enabledForPrompt: true, + })) + + mockMcpServers.mockReturnValue([ + { + name: "server1", + status: "connected", + disabled: false, + tools, + }, + ]) + + render() + + expect(screen.getByText("Too many tools enabled")).toBeInTheDocument() + expect( + screen.getByText( + `You have ${MAX_MCP_TOOLS_THRESHOLD + 10} tools enabled via 1 MCP servers. Such a high number can confuse the model and lead to errors. Try to keep it below ${MAX_MCP_TOOLS_THRESHOLD}.`, + ), + ).toBeInTheDocument() + }) + + it("ignores disabled servers", () => { + // Create tools across two servers, one disabled + const tools = Array.from({ length: MAX_MCP_TOOLS_THRESHOLD + 10 }, (_, i) => ({ + name: `tool${i}`, + enabledForPrompt: true, + })) + + mockMcpServers.mockReturnValue([ + { + name: "disabledServer", + status: "connected", + disabled: true, // This server is disabled + tools, + }, + { + name: "enabledServer", + status: "connected", + disabled: false, + tools: [{ name: "tool1", enabledForPrompt: true }], // Only 1 tool + }, + ]) + + const { container } = render() + + // Should not render because only 1 tool is on enabled server + expect(container.firstChild).toBeNull() + }) + + it("ignores disconnected servers", () => { + const tools = Array.from({ length: MAX_MCP_TOOLS_THRESHOLD + 10 }, (_, i) => ({ + name: `tool${i}`, + enabledForPrompt: true, + })) + + mockMcpServers.mockReturnValue([ + { + name: "disconnectedServer", + status: "disconnected", // Not connected + disabled: false, + tools, + }, + ]) + + const { container } = render() + + expect(container.firstChild).toBeNull() + }) + + it("ignores disabled tools", () => { + // Create tools with some disabled + const enabledTools = Array.from({ length: 20 }, (_, i) => ({ + name: `enabledTool${i}`, + enabledForPrompt: true, + })) + const disabledTools = Array.from({ length: MAX_MCP_TOOLS_THRESHOLD + 10 }, (_, i) => ({ + name: `disabledTool${i}`, + enabledForPrompt: false, // These are disabled + })) + + mockMcpServers.mockReturnValue([ + { + name: "server1", + status: "connected", + disabled: false, + tools: [...enabledTools, ...disabledTools], + }, + ]) + + const { container } = render() + + // Should not render because only 20 tools are enabled + expect(container.firstChild).toBeNull() + }) + + it("treats tools with undefined enabledForPrompt as enabled", () => { + // Create tools without enabledForPrompt set (default behavior is enabled) + const tools = Array.from({ length: MAX_MCP_TOOLS_THRESHOLD + 5 }, (_, i) => ({ + name: `tool${i}`, + // enabledForPrompt is undefined, which means enabled by default + })) + + mockMcpServers.mockReturnValue([ + { + name: "server1", + status: "connected", + disabled: false, + tools, + }, + ]) + + render() + + expect(screen.getByText("Too many tools enabled")).toBeInTheDocument() + }) + + it("counts tools across multiple servers", () => { + // Create tools across multiple servers + const tools1 = Array.from({ length: 25 }, (_, i) => ({ + name: `server1tool${i}`, + enabledForPrompt: true, + })) + const tools2 = Array.from({ length: 20 }, (_, i) => ({ + name: `server2tool${i}`, + enabledForPrompt: true, + })) + + mockMcpServers.mockReturnValue([ + { + name: "server1", + status: "connected", + disabled: false, + tools: tools1, + }, + { + name: "server2", + status: "connected", + disabled: false, + tools: tools2, + }, + ]) + + render() + + // 25 + 20 = 45 tools > 40 threshold + expect(screen.getByText("Too many tools enabled")).toBeInTheDocument() + expect( + screen.getByText( + `You have 45 tools enabled via 2 MCP servers. Such a high number can confuse the model and lead to errors. Try to keep it below ${MAX_MCP_TOOLS_THRESHOLD}.`, + ), + ).toBeInTheDocument() + }) + + it("exports MAX_MCP_TOOLS_THRESHOLD constant", () => { + expect(MAX_MCP_TOOLS_THRESHOLD).toBe(40) + }) +}) diff --git a/webview-ui/src/components/chat/__tests__/WarningRow.spec.tsx b/webview-ui/src/components/chat/__tests__/WarningRow.spec.tsx new file mode 100644 index 00000000000..13e4e20f508 --- /dev/null +++ b/webview-ui/src/components/chat/__tests__/WarningRow.spec.tsx @@ -0,0 +1,71 @@ +import { render, screen, fireEvent } from "@/utils/test-utils" +import { vscode } from "@/utils/vscode" + +import { WarningRow } from "../WarningRow" + +// Mock vscode webview messaging +vi.mock("@/utils/vscode", () => ({ + vscode: { + postMessage: vi.fn(), + }, +})) + +// Mock i18n TranslationContext +vi.mock("@/i18n/TranslationContext", () => ({ + useAppTranslation: () => ({ + t: (key: string) => { + const map: Record = { + "chat:apiRequest.errorMessage.docs": "Docs", + } + return map[key] ?? key + }, + }), +})) + +describe("WarningRow", () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + it("renders title and message", () => { + render() + + expect(screen.getByText("Test Warning")).toBeInTheDocument() + expect(screen.getByText("This is a test warning message")).toBeInTheDocument() + }) + + it("does not render docs link when docsURL is not provided", () => { + render() + + expect(screen.queryByText("Docs")).not.toBeInTheDocument() + }) + + it("renders docs link when docsURL is provided", () => { + render() + + const docsLink = screen.getByText("Docs") + expect(docsLink).toBeInTheDocument() + }) + + it("opens external URL when docs link is clicked", () => { + const mockPostMessage = vi.mocked(vscode.postMessage) + + render() + + const docsLink = screen.getByText("Docs") + fireEvent.click(docsLink) + + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "openExternal", + url: "https://docs.example.com", + }) + }) + + it("renders warning icon", () => { + const { container } = render() + + // TriangleAlert icon should be present (as an SVG element) + const warningIcon = container.querySelector("svg") + expect(warningIcon).toBeInTheDocument() + }) +}) diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index 3ab2c037af2..8d7e0b03189 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -484,5 +484,11 @@ "updated": "Updated the to-do list", "completed": "Completed", "started": "Started" + }, + "tooManyTools": { + "title": "Too many tools enabled", + "message_one": "You have {{toolCount}} tool enabled via {{serverCount}} MCP server. Such a high number can confuse the model and lead to errors. Try to keep it below {{threshold}}.", + "message_other": "You have {{toolCount}} tools enabled via {{serverCount}} MCP servers. Such a high number can confuse the model and lead to errors. Try to keep it below {{threshold}}.", + "message": "You have {{toolCount}} tools enabled via {{serverCount}} MCP servers. Such a high number can confuse the model and lead to errors. Try to keep it below {{threshold}}." } } From 5a4773e8f7574cb55b1b779670c65250d2d6863c Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Fri, 16 Jan 2026 09:52:17 +0000 Subject: [PATCH 2/9] Moves constant to the right place --- apps/cli/src/types/constants.ts | 5 ++++- packages/types/src/mcp.ts | 6 ++++++ webview-ui/src/components/chat/ErrorRow.tsx | 2 +- webview-ui/src/components/chat/TooManyToolsWarning.tsx | 8 +++----- webview-ui/src/components/chat/WarningRow.tsx | 4 ++-- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/apps/cli/src/types/constants.ts b/apps/cli/src/types/constants.ts index 5b3dc577786..361413c0f17 100644 --- a/apps/cli/src/types/constants.ts +++ b/apps/cli/src/types/constants.ts @@ -1,4 +1,7 @@ -import { reasoningEffortsExtended } from "@roo-code/types" +import { reasoningEffortsExtended, MAX_MCP_TOOLS_THRESHOLD } from "@roo-code/types" + +// Re-export for backward compatibility +export { MAX_MCP_TOOLS_THRESHOLD } export const DEFAULT_FLAGS = { mode: "code", diff --git a/packages/types/src/mcp.ts b/packages/types/src/mcp.ts index 92e238efbb1..fe725934c24 100644 --- a/packages/types/src/mcp.ts +++ b/packages/types/src/mcp.ts @@ -1,5 +1,11 @@ import { z } from "zod" +/** + * Maximum number of MCP tools that can be enabled before showing a warning. + * LLMs tend to perform poorly when given too many tools to choose from. + */ +export const MAX_MCP_TOOLS_THRESHOLD = 40 + /** * McpServerUse */ diff --git a/webview-ui/src/components/chat/ErrorRow.tsx b/webview-ui/src/components/chat/ErrorRow.tsx index 50e7c67b5bb..93a5f341e49 100644 --- a/webview-ui/src/components/chat/ErrorRow.tsx +++ b/webview-ui/src/components/chat/ErrorRow.tsx @@ -266,7 +266,7 @@ export const ErrorRow = memo(
)} -
+

= ({ title, message, docsURL const { t } = useAppTranslation() return ( -

+
{title} @@ -45,7 +45,7 @@ export const WarningRow: React.FC = ({ title, message, docsURL )}
-
+

{message}

From a3ceff5f00c6044336ed0307dfe7512ca46178e5 Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Fri, 16 Jan 2026 10:09:36 +0000 Subject: [PATCH 3/9] Move it to the backend --- packages/types/src/message.ts | 2 + src/core/task/Task.ts | 72 +++++++++++++++++++ webview-ui/src/components/chat/ChatRow.tsx | 19 +++++ webview-ui/src/components/chat/ChatView.tsx | 5 -- webview-ui/src/components/chat/ErrorRow.tsx | 2 +- webview-ui/src/components/chat/WarningRow.tsx | 2 +- 6 files changed, 95 insertions(+), 7 deletions(-) diff --git a/packages/types/src/message.ts b/packages/types/src/message.ts index 109cd842bac..d6dd46099ad 100644 --- a/packages/types/src/message.ts +++ b/packages/types/src/message.ts @@ -149,6 +149,7 @@ export function isNonBlockingAsk(ask: ClineAsk): ask is NonBlockingAsk { * - `condense_context`: Context condensation/summarization has started * - `condense_context_error`: Error occurred during context condensation * - `codebase_search_result`: Results from searching the codebase + * - `too_many_tools_warning`: Warning that too many MCP tools are enabled, which may confuse the LLM */ export const clineSays = [ "error", @@ -180,6 +181,7 @@ export const clineSays = [ "sliding_window_truncation", "codebase_search_result", "user_edit_todos", + "too_many_tools_warning", ] as const export const clineSaySchema = z.enum(clineSays) diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 3acb6c24918..5ec26d5ba2f 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -53,6 +53,7 @@ import { MIN_CHECKPOINT_TIMEOUT_SECONDS, TOOL_PROTOCOL, ConsecutiveMistakeError, + MAX_MCP_TOOLS_THRESHOLD, } from "@roo-code/types" import { TelemetryService } from "@roo-code/telemetry" import { CloudService, BridgeOrchestrator } from "@roo-code/cloud" @@ -1832,6 +1833,59 @@ export class Task extends EventEmitter implements TaskLike { // Lifecycle // Start / Resume / Abort / Dispose + /** + * Count the number of enabled MCP tools across all enabled and connected servers. + * Returns the count along with the number of servers contributing. + * + * @returns Object with enabledToolCount and enabledServerCount + */ + private async countEnabledMcpTools(): Promise<{ enabledToolCount: number; enabledServerCount: number }> { + let serverCount = 0 + let toolCount = 0 + + try { + const provider = this.providerRef.deref() + if (!provider) { + return { enabledToolCount: 0, enabledServerCount: 0 } + } + + const { mcpEnabled } = (await provider.getState()) ?? {} + if (!(mcpEnabled ?? true)) { + return { enabledToolCount: 0, enabledServerCount: 0 } + } + + const mcpHub = await McpServerManager.getInstance(provider.context, provider) + if (!mcpHub) { + return { enabledToolCount: 0, enabledServerCount: 0 } + } + + const servers = mcpHub.getServers() + for (const server of servers) { + // Skip disabled servers + if (server.disabled) continue + + // Skip servers that are not connected + if (server.status !== "connected") continue + + serverCount++ + + // Count enabled tools on this server + if (server.tools) { + for (const tool of server.tools) { + // Tool is enabled if enabledForPrompt is undefined (default) or true + if (tool.enabledForPrompt !== false) { + toolCount++ + } + } + } + } + } catch (error) { + console.error("[Task#countEnabledMcpTools] Error counting MCP tools:", error) + } + + return { enabledToolCount: toolCount, enabledServerCount: serverCount } + } + private async startTask(task?: string, images?: string[]): Promise { if (this.enableBridge) { try { @@ -1858,6 +1912,24 @@ export class Task extends EventEmitter implements TaskLike { await this.providerRef.deref()?.postStateToWebview() await this.say("text", task, images) + + // Check for too many MCP tools and warn the user + const { enabledToolCount, enabledServerCount } = await this.countEnabledMcpTools() + if (enabledToolCount > MAX_MCP_TOOLS_THRESHOLD) { + await this.say( + "too_many_tools_warning", + JSON.stringify({ + toolCount: enabledToolCount, + serverCount: enabledServerCount, + threshold: MAX_MCP_TOOLS_THRESHOLD, + }), + undefined, + undefined, + undefined, + undefined, + { isNonInteractive: true }, + ) + } this.isInitialized = true let imageBlocks: Anthropic.ImageBlockParam[] = formatResponse.imageBlocks(images) diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 24749bb4191..025cf1a1810 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -32,6 +32,7 @@ import { ReasoningBlock } from "./ReasoningBlock" import Thumbnails from "../common/Thumbnails" import ImageBlock from "../common/ImageBlock" import ErrorRow from "./ErrorRow" +import WarningRow from "./WarningRow" import McpResourceRow from "../mcp/McpResourceRow" @@ -1512,6 +1513,24 @@ export const ChatRowContent = ({ case "browser_action_result": // Handled by BrowserSessionRow; prevent raw JSON (action/result) from rendering here return null + case "too_many_tools_warning": { + const warningData = safeJsonParse<{ + toolCount: number + serverCount: number + threshold: number + }>(message.text || "{}") + if (!warningData) return null + return ( + + ) + } default: return ( <> diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index e8d1331784e..81f6cbebf66 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -44,7 +44,6 @@ import TaskHeader from "./TaskHeader" import SystemPromptWarning from "./SystemPromptWarning" import ProfileViolationWarning from "./ProfileViolationWarning" import { CheckpointWarning } from "./CheckpointWarning" -import TooManyToolsWarning from "./TooManyToolsWarning" import { QueuedMessages } from "./QueuedMessages" import DismissibleUpsell from "../common/DismissibleUpsell" import { useCloudUpsell } from "@src/hooks/useCloudUpsell" @@ -1508,10 +1507,6 @@ const ChatViewComponent: React.ForwardRefRenderFunction
)} - -
- -
) : (
diff --git a/webview-ui/src/components/chat/ErrorRow.tsx b/webview-ui/src/components/chat/ErrorRow.tsx index 93a5f341e49..70253504247 100644 --- a/webview-ui/src/components/chat/ErrorRow.tsx +++ b/webview-ui/src/components/chat/ErrorRow.tsx @@ -270,7 +270,7 @@ export const ErrorRow = memo(

{message} {formattedErrorDetails && ( diff --git a/webview-ui/src/components/chat/WarningRow.tsx b/webview-ui/src/components/chat/WarningRow.tsx index c48bafb22b8..a9527c24e77 100644 --- a/webview-ui/src/components/chat/WarningRow.tsx +++ b/webview-ui/src/components/chat/WarningRow.tsx @@ -45,7 +45,7 @@ export const WarningRow: React.FC = ({ title, message, docsURL )}

-
+

{message}

From 18bcd221e636e74ab5072fa733e79b5c891717f3 Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Fri, 16 Jan 2026 10:22:25 +0000 Subject: [PATCH 4/9] i18n --- webview-ui/src/components/chat/ChatRow.tsx | 8 +++++--- .../src/components/chat/TooManyToolsWarning.tsx | 8 +++++--- .../chat/__tests__/TooManyToolsWarning.spec.tsx | 14 +++++++++++--- webview-ui/src/i18n/locales/ca/chat.json | 8 ++++++++ webview-ui/src/i18n/locales/de/chat.json | 8 ++++++++ webview-ui/src/i18n/locales/en/chat.json | 8 +++++--- webview-ui/src/i18n/locales/es/chat.json | 8 ++++++++ webview-ui/src/i18n/locales/fr/chat.json | 8 ++++++++ webview-ui/src/i18n/locales/hi/chat.json | 8 ++++++++ webview-ui/src/i18n/locales/id/chat.json | 8 ++++++++ webview-ui/src/i18n/locales/it/chat.json | 8 ++++++++ webview-ui/src/i18n/locales/ja/chat.json | 8 ++++++++ webview-ui/src/i18n/locales/ko/chat.json | 8 ++++++++ webview-ui/src/i18n/locales/nl/chat.json | 8 ++++++++ webview-ui/src/i18n/locales/pl/chat.json | 8 ++++++++ webview-ui/src/i18n/locales/pt-BR/chat.json | 8 ++++++++ webview-ui/src/i18n/locales/ru/chat.json | 8 ++++++++ webview-ui/src/i18n/locales/tr/chat.json | 8 ++++++++ webview-ui/src/i18n/locales/vi/chat.json | 8 ++++++++ webview-ui/src/i18n/locales/zh-CN/chat.json | 8 ++++++++ webview-ui/src/i18n/locales/zh-TW/chat.json | 8 ++++++++ 21 files changed, 162 insertions(+), 12 deletions(-) diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 025cf1a1810..f453ad25f8d 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -1520,12 +1520,14 @@ export const ChatRowContent = ({ threshold: number }>(message.text || "{}") if (!warningData) return null + const toolsPart = t("chat:tooManyTools.toolsPart", { count: warningData.toolCount }) + const serversPart = t("chat:tooManyTools.serversPart", { count: warningData.serverCount }) return ( diff --git a/webview-ui/src/components/chat/TooManyToolsWarning.tsx b/webview-ui/src/components/chat/TooManyToolsWarning.tsx index f73c7af099b..68181bc26f7 100644 --- a/webview-ui/src/components/chat/TooManyToolsWarning.tsx +++ b/webview-ui/src/components/chat/TooManyToolsWarning.tsx @@ -53,9 +53,11 @@ export const TooManyToolsWarning: React.FC = () => { return null } - const message = t("chat:tooManyTools.message", { - toolCount: enabledToolCount, - serverCount: enabledServerCount, + const toolsPart = t("chat:tooManyTools.toolsPart", { count: enabledToolCount }) + const serversPart = t("chat:tooManyTools.serversPart", { count: enabledServerCount }) + const message = t("chat:tooManyTools.messageTemplate", { + tools: toolsPart, + servers: serversPart, threshold: MAX_MCP_TOOLS_THRESHOLD, }) diff --git a/webview-ui/src/components/chat/__tests__/TooManyToolsWarning.spec.tsx b/webview-ui/src/components/chat/__tests__/TooManyToolsWarning.spec.tsx index b000aec7e6e..dd9c209a092 100644 --- a/webview-ui/src/components/chat/__tests__/TooManyToolsWarning.spec.tsx +++ b/webview-ui/src/components/chat/__tests__/TooManyToolsWarning.spec.tsx @@ -25,8 +25,16 @@ vi.mock("@/i18n/TranslationContext", () => ({ if (key === "chat:tooManyTools.title") { return "Too many tools enabled" } - if (key === "chat:tooManyTools.message") { - return `You have ${params?.toolCount} tools enabled via ${params?.serverCount} MCP servers. Such a high number can confuse the model and lead to errors. Try to keep it below ${params?.threshold}.` + if (key === "chat:tooManyTools.toolsPart") { + const count = params?.count ?? 0 + return count === 1 ? `${count} tool` : `${count} tools` + } + if (key === "chat:tooManyTools.serversPart") { + const count = params?.count ?? 0 + return count === 1 ? `${count} MCP server` : `${count} MCP servers` + } + if (key === "chat:tooManyTools.messageTemplate") { + return `You have ${params?.tools} enabled via ${params?.servers}. Such a high number can confuse the model and lead to errors. Try to keep it below ${params?.threshold}.` } if (key === "chat:apiRequest.errorMessage.docs") { return "Docs" @@ -110,7 +118,7 @@ describe("TooManyToolsWarning", () => { expect(screen.getByText("Too many tools enabled")).toBeInTheDocument() expect( screen.getByText( - `You have ${MAX_MCP_TOOLS_THRESHOLD + 10} tools enabled via 1 MCP servers. Such a high number can confuse the model and lead to errors. Try to keep it below ${MAX_MCP_TOOLS_THRESHOLD}.`, + `You have ${MAX_MCP_TOOLS_THRESHOLD + 10} tools enabled via 1 MCP server. Such a high number can confuse the model and lead to errors. Try to keep it below ${MAX_MCP_TOOLS_THRESHOLD}.`, ), ).toBeInTheDocument() }) diff --git a/webview-ui/src/i18n/locales/ca/chat.json b/webview-ui/src/i18n/locales/ca/chat.json index 650a4949a2c..ac46cea9f2f 100644 --- a/webview-ui/src/i18n/locales/ca/chat.json +++ b/webview-ui/src/i18n/locales/ca/chat.json @@ -492,5 +492,13 @@ "updated": "S'ha actualitzat la llista de tasques pendents", "completed": "Completat", "started": "Iniciat" + }, + "tooManyTools": { + "title": "Massa eines habilitades", + "toolsPart_one": "{{count}} eina", + "toolsPart_other": "{{count}} eines", + "serversPart_one": "{{count}} servidor MCP", + "serversPart_other": "{{count}} servidors MCP", + "messageTemplate": "Tens {{tools}} habilitades via {{servers}}. Un nombre tant alt pot confondre el model i portar a errors. Intenta mantenir-lo per sota de {{threshold}}." } } diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index fc251576346..e5d7e925407 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -492,5 +492,13 @@ "updated": "Die To-Do-Liste wurde aktualisiert", "completed": "Abgeschlossen", "started": "Gestartet" + }, + "tooManyTools": { + "title": "Zu viele Tools aktiviert", + "toolsPart_one": "{{count}} Tool", + "toolsPart_other": "{{count}} Tools", + "serversPart_one": "{{count}} MCP-Server", + "serversPart_other": "{{count}} MCP-Server", + "messageTemplate": "Du hast {{tools}} über {{servers}} aktiviert. Eine so hohe Anzahl kann das Modell verwirren und zu Fehlern führen. Versuche, es unter {{threshold}} zu halten." } } diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index 8d7e0b03189..1c74d4bbe75 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -487,8 +487,10 @@ }, "tooManyTools": { "title": "Too many tools enabled", - "message_one": "You have {{toolCount}} tool enabled via {{serverCount}} MCP server. Such a high number can confuse the model and lead to errors. Try to keep it below {{threshold}}.", - "message_other": "You have {{toolCount}} tools enabled via {{serverCount}} MCP servers. Such a high number can confuse the model and lead to errors. Try to keep it below {{threshold}}.", - "message": "You have {{toolCount}} tools enabled via {{serverCount}} MCP servers. Such a high number can confuse the model and lead to errors. Try to keep it below {{threshold}}." + "toolsPart_one": "{{count}} tool", + "toolsPart_other": "{{count}} tools", + "serversPart_one": "{{count}} MCP server", + "serversPart_other": "{{count}} MCP servers", + "messageTemplate": "You have {{tools}} enabled via {{servers}}. Such a high number can confuse the model and lead to errors. Try to keep it below {{threshold}}." } } diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index 09bb9c80aae..1df3bcd80f5 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -492,5 +492,13 @@ "updated": "Se actualizó la lista de tareas pendientes", "completed": "Completado", "started": "Iniciado" + }, + "tooManyTools": { + "title": "Demasiadas herramientas habilitadas", + "toolsPart_one": "{{count}} herramienta", + "toolsPart_other": "{{count}} herramientas", + "serversPart_one": "{{count}} servidor MCP", + "serversPart_other": "{{count}} servidores MCP", + "messageTemplate": "Tienes {{tools}} habilitadas a través de {{servers}}. Un número tan alto puede confundir al modelo y llevar a errores. Intenta mantenerlo por debajo de {{threshold}}." } } diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index 700b1534337..c3d36d96c95 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -492,5 +492,13 @@ "updated": "La liste des tâches a été mise à jour", "completed": "Terminé", "started": "Commencé" + }, + "tooManyTools": { + "title": "Trop d'outils activés", + "toolsPart_one": "{{count}} outil", + "toolsPart_other": "{{count}} outils", + "serversPart_one": "{{count}} serveur MCP", + "serversPart_other": "{{count}} serveurs MCP", + "messageTemplate": "Tu as {{tools}} activés via {{servers}}. Un nombre aussi élevé peut confondre le modèle et entraîner des erreurs. Essaie de le maintenir en dessous de {{threshold}}." } } diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json index 753fc857168..545aefc8ae6 100644 --- a/webview-ui/src/i18n/locales/hi/chat.json +++ b/webview-ui/src/i18n/locales/hi/chat.json @@ -492,5 +492,13 @@ "updated": "टू-डू सूची अपडेट की गई", "completed": "पूरा हुआ", "started": "शुरू हुआ" + }, + "tooManyTools": { + "title": "बहुत सारे उपकरण सक्षम हैं", + "toolsPart_one": "{{count}} उपकरण", + "toolsPart_other": "{{count}} उपकरण", + "serversPart_one": "{{count}} MCP सर्वर", + "serversPart_other": "{{count}} MCP सर्वर", + "messageTemplate": "आपके पास {{servers}} के माध्यम से {{tools}} सक्षम हैं। इतनी अधिक संख्या मॉडल को भ्रमित कर सकती है और त्रुटियों का कारण बन सकती है। इसे {{threshold}} से नीचे रखने का प्रयास करें।" } } diff --git a/webview-ui/src/i18n/locales/id/chat.json b/webview-ui/src/i18n/locales/id/chat.json index 04db7af45e8..c341f7a92fa 100644 --- a/webview-ui/src/i18n/locales/id/chat.json +++ b/webview-ui/src/i18n/locales/id/chat.json @@ -498,5 +498,13 @@ "updated": "Memperbarui daftar to-do", "completed": "Selesai", "started": "Dimulai" + }, + "tooManyTools": { + "title": "Terlalu banyak alat diaktifkan", + "toolsPart_one": "{{count}} alat", + "toolsPart_other": "{{count}} alat", + "serversPart_one": "{{count}} server MCP", + "serversPart_other": "{{count}} server MCP", + "messageTemplate": "Anda memiliki {{tools}} diaktifkan melalui {{servers}}. Jumlah yang begitu besar dapat membingungkan model dan menyebabkan kesalahan. Cobalah untuk menjaganya di bawah {{threshold}}." } } diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index 73ed761b293..c841db324e6 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -492,5 +492,13 @@ "updated": "Aggiornata la lista delle cose da fare", "completed": "Completato", "started": "Iniziato" + }, + "tooManyTools": { + "title": "Troppi strumenti abilitati", + "toolsPart_one": "{{count}} strumento", + "toolsPart_other": "{{count}} strumenti", + "serversPart_one": "{{count}} server MCP", + "serversPart_other": "{{count}} server MCP", + "messageTemplate": "Hai {{tools}} abilitate via {{servers}}. Un numero così alto può confondere il modello e portare a errori. Prova a mantenerlo sotto {{threshold}}." } } diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json index 44543b22acd..6eb8d62eb35 100644 --- a/webview-ui/src/i18n/locales/ja/chat.json +++ b/webview-ui/src/i18n/locales/ja/chat.json @@ -492,5 +492,13 @@ "updated": "To-Doリストを更新しました", "completed": "完了", "started": "開始" + }, + "tooManyTools": { + "title": "有効になっているツールが多すぎます", + "toolsPart_one": "{{count}} ツール", + "toolsPart_other": "{{count}} ツール", + "serversPart_one": "{{count}} MCP サーバー", + "serversPart_other": "{{count}} MCP サーバー", + "messageTemplate": "{{servers}}経由で{{tools}}が有効になっています。このような高い数は、モデルを混乱させてエラーを引き起こす可能性があります。{{threshold}}以下に保つようにしてください。" } } diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json index d758f082df0..8bf97b87692 100644 --- a/webview-ui/src/i18n/locales/ko/chat.json +++ b/webview-ui/src/i18n/locales/ko/chat.json @@ -492,5 +492,13 @@ "updated": "할 일 목록을 업데이트했습니다", "completed": "완료됨", "started": "시작됨" + }, + "tooManyTools": { + "title": "활성화된 도구가 너무 많습니다", + "toolsPart_one": "{{count}}개 도구", + "toolsPart_other": "{{count}}개 도구", + "serversPart_one": "{{count}}개 MCP 서버", + "serversPart_other": "{{count}}개 MCP 서버", + "messageTemplate": "{{servers}}를 통해 {{tools}}가 활성화되어 있습니다. 이렇게 많은 수의 도구는 모델을 혼동시키고 오류를 유발할 수 있습니다. {{threshold}} 이하로 유지하도록 노력하세요." } } diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json index 6f98a005382..3c56682d873 100644 --- a/webview-ui/src/i18n/locales/nl/chat.json +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -492,5 +492,13 @@ "updated": "De to-do-lijst is bijgewerkt", "completed": "Voltooid", "started": "Gestart" + }, + "tooManyTools": { + "title": "Te veel tools ingeschakeld", + "toolsPart_one": "{{count}} tool", + "toolsPart_other": "{{count}} tools", + "serversPart_one": "{{count}} MCP server", + "serversPart_other": "{{count}} MCP servers", + "messageTemplate": "Je hebt {{tools}} ingeschakeld via {{servers}}. Zoveel tools kunnen het model verwarren en tot fouten leiden. Probeer dit onder {{threshold}} te houden." } } diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index eaac2b46693..d9f8a976dab 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -492,5 +492,13 @@ "updated": "Zaktualizowano listę zadań do wykonania", "completed": "Ukończono", "started": "Rozpoczęto" + }, + "tooManyTools": { + "title": "Włączono zbyt wiele narzędzi", + "toolsPart_one": "{{count}} narzędzie", + "toolsPart_other": "{{count}} narzędzi", + "serversPart_one": "{{count}} serwer MCP", + "serversPart_other": "{{count}} serwerów MCP", + "messageTemplate": "Masz {{tools}} włączonych przez {{servers}}. Taka duża liczba może zamieszać model i prowadzić do błędów. Staraj się, aby była poniżej {{threshold}}." } } diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index 555da7dca85..57147536594 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -492,5 +492,13 @@ "updated": "A lista de tarefas foi atualizada", "completed": "Concluído", "started": "Iniciado" + }, + "tooManyTools": { + "title": "Muitas ferramentas habilitadas", + "toolsPart_one": "{{count}} ferramenta", + "toolsPart_other": "{{count}} ferramentas", + "serversPart_one": "{{count}} servidor MCP", + "serversPart_other": "{{count}} servidores MCP", + "messageTemplate": "Você tem {{tools}} habilitadas via {{servers}}. Um número tão alto pode confundir o modelo e levar a erros. Tente mantê-lo abaixo de {{threshold}}." } } diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index 7d688361e76..97bd54c638b 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -493,5 +493,13 @@ "updated": "Список задач обновлен", "completed": "Завершено", "started": "Начато" + }, + "tooManyTools": { + "title": "Включено слишком много инструментов", + "toolsPart_one": "{{count}} инструмент", + "toolsPart_other": "{{count}} инструментов", + "serversPart_one": "{{count}} сервер MCP", + "serversPart_other": "{{count}} серверов MCP", + "messageTemplate": "У тебя включено {{tools}} через {{servers}}. Такое большое количество может сбить модель с толку и привести к ошибкам. Постарайся держать это ниже {{threshold}}." } } diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index 733c612564c..93f1e6572ea 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -493,5 +493,13 @@ "updated": "Yapılacaklar listesi güncellendi", "completed": "Tamamlandı", "started": "Başladı" + }, + "tooManyTools": { + "title": "Çok fazla araç etkinleştirildi", + "toolsPart_one": "{{count}} araç", + "toolsPart_other": "{{count}} araç", + "serversPart_one": "{{count}} MCP sunucusu", + "serversPart_other": "{{count}} MCP sunucusu", + "messageTemplate": "{{servers}} üzerinden {{tools}} etkinleştirilmiş durumda. Bu kadar fazlası modeli kafası karışabilir ve hatalara neden olabilir. {{threshold}} altında tutmaya çalış." } } diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index 1f253189646..551672f37a4 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -493,5 +493,13 @@ "updated": "Đã cập nhật danh sách công việc", "completed": "Đã hoàn thành", "started": "Đã bắt đầu" + }, + "tooManyTools": { + "title": "Đã bật quá nhiều công cụ", + "toolsPart_one": "{{count}} công cụ", + "toolsPart_other": "{{count}} công cụ", + "serversPart_one": "{{count}} máy chủ MCP", + "serversPart_other": "{{count}} máy chủ MCP", + "messageTemplate": "Bạn đã bật {{tools}} qua {{servers}}. Số lượng lớn như vậy có thể khiến mô hình bối rối và dẫn đến lỗi. Cố gắng giữ nó dưới {{threshold}}." } } diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index a075f2686aa..fdb1b8c92e0 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -493,5 +493,13 @@ "updated": "已更新待办事项列表", "completed": "已完成", "started": "已开始" + }, + "tooManyTools": { + "title": "启用的工具过多", + "toolsPart_one": "{{count}} 个工具", + "toolsPart_other": "{{count}} 个工具", + "serversPart_one": "{{count}} 个 MCP 服务", + "serversPart_other": "{{count}} 个 MCP 服务", + "messageTemplate": "你通过 {{servers}} 启用了 {{tools}}。这么多数量会混淆模型并导致错误。建议将其保持在 {{threshold}} 以下。" } } diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index 826178ec06f..92f210f96f9 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -493,5 +493,13 @@ "updated": "已更新待辦事項列表", "completed": "已完成", "started": "已開始" + }, + "tooManyTools": { + "title": "啟用的工具過多", + "toolsPart_one": "{{count}} 個工具", + "toolsPart_other": "{{count}} 個工具", + "serversPart_one": "{{count}} 個 MCP 伺服器", + "serversPart_other": "{{count}} 個 MCP 伺服器", + "messageTemplate": "你已啟用 {{tools}}(透過 {{servers}})。這麼多的工具可能會混淆模型並導致錯誤。請嘗試保持在 {{threshold}} 以下。" } } From c30bdeef6ca5d2ae5dfdd6cf98de9391c09dcc15 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Sun, 18 Jan 2026 08:29:46 -0500 Subject: [PATCH 5/9] Add actionlink that takes you to MCP settings in this case --- apps/cli/src/types/constants.ts | 5 +-- webview-ui/src/components/chat/ChatRow.tsx | 7 +++ .../components/chat/TooManyToolsWarning.tsx | 18 +++++--- webview-ui/src/components/chat/WarningRow.tsx | 24 ++++++++++- .../__tests__/TooManyToolsWarning.spec.tsx | 43 +++++++++++++++++-- .../chat/__tests__/WarningRow.spec.tsx | 38 ++++++++++++++++ webview-ui/src/i18n/locales/ca/chat.json | 3 +- webview-ui/src/i18n/locales/de/chat.json | 3 +- webview-ui/src/i18n/locales/en/chat.json | 3 +- webview-ui/src/i18n/locales/es/chat.json | 3 +- webview-ui/src/i18n/locales/fr/chat.json | 3 +- webview-ui/src/i18n/locales/hi/chat.json | 3 +- webview-ui/src/i18n/locales/id/chat.json | 3 +- webview-ui/src/i18n/locales/it/chat.json | 3 +- webview-ui/src/i18n/locales/ja/chat.json | 3 +- webview-ui/src/i18n/locales/ko/chat.json | 3 +- webview-ui/src/i18n/locales/nl/chat.json | 3 +- webview-ui/src/i18n/locales/pl/chat.json | 3 +- webview-ui/src/i18n/locales/pt-BR/chat.json | 3 +- webview-ui/src/i18n/locales/ru/chat.json | 3 +- webview-ui/src/i18n/locales/tr/chat.json | 3 +- webview-ui/src/i18n/locales/vi/chat.json | 3 +- webview-ui/src/i18n/locales/zh-CN/chat.json | 3 +- webview-ui/src/i18n/locales/zh-TW/chat.json | 3 +- 24 files changed, 156 insertions(+), 33 deletions(-) diff --git a/apps/cli/src/types/constants.ts b/apps/cli/src/types/constants.ts index 361413c0f17..5b3dc577786 100644 --- a/apps/cli/src/types/constants.ts +++ b/apps/cli/src/types/constants.ts @@ -1,7 +1,4 @@ -import { reasoningEffortsExtended, MAX_MCP_TOOLS_THRESHOLD } from "@roo-code/types" - -// Re-export for backward compatibility -export { MAX_MCP_TOOLS_THRESHOLD } +import { reasoningEffortsExtended } from "@roo-code/types" export const DEFAULT_FLAGS = { mode: "code", diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index f453ad25f8d..eb00cd3671d 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -1530,6 +1530,13 @@ export const ChatRowContent = ({ servers: serversPart, threshold: warningData.threshold, })} + actionText={t("chat:tooManyTools.openMcpSettings")} + onAction={() => + window.postMessage( + { type: "action", action: "settingsButtonClicked", values: { section: "mcp" } }, + "*", + ) + } /> ) } diff --git a/webview-ui/src/components/chat/TooManyToolsWarning.tsx b/webview-ui/src/components/chat/TooManyToolsWarning.tsx index 68181bc26f7..4afffa354c2 100644 --- a/webview-ui/src/components/chat/TooManyToolsWarning.tsx +++ b/webview-ui/src/components/chat/TooManyToolsWarning.tsx @@ -1,12 +1,9 @@ -import React, { useMemo } from "react" +import React, { useCallback, useMemo } from "react" import { useAppTranslation } from "@/i18n/TranslationContext" import { useExtensionState } from "@src/context/ExtensionStateContext" import { MAX_MCP_TOOLS_THRESHOLD } from "@roo-code/types" import WarningRow from "./WarningRow" -// Re-export for backward compatibility -export { MAX_MCP_TOOLS_THRESHOLD } - /** * Displays a warning when the user has too many MCP tools enabled. * LLMs get confused when offered too many tools, which can lead to errors. @@ -48,6 +45,10 @@ export const TooManyToolsWarning: React.FC = () => { return { enabledServerCount: serverCount, enabledToolCount: toolCount } }, [mcpServers]) + const handleOpenMcpSettings = useCallback(() => { + window.postMessage({ type: "action", action: "settingsButtonClicked", values: { section: "mcp" } }, "*") + }, []) + // Don't show warning if under threshold if (enabledToolCount <= MAX_MCP_TOOLS_THRESHOLD) { return null @@ -61,7 +62,14 @@ export const TooManyToolsWarning: React.FC = () => { threshold: MAX_MCP_TOOLS_THRESHOLD, }) - return + return ( + + ) } export default TooManyToolsWarning diff --git a/webview-ui/src/components/chat/WarningRow.tsx b/webview-ui/src/components/chat/WarningRow.tsx index a9527c24e77..3fe4e900769 100644 --- a/webview-ui/src/components/chat/WarningRow.tsx +++ b/webview-ui/src/components/chat/WarningRow.tsx @@ -7,24 +7,30 @@ export interface WarningRowProps { title: string message: string docsURL?: string + actionText?: string + onAction?: () => void } /** * A generic warning row component that displays a warning icon, title, and message. - * Optionally includes a documentation link. + * Optionally includes a documentation link and/or an action link. * * @param title - The warning title displayed in bold * @param message - The warning message displayed below the title * @param docsURL - Optional documentation link URL (shown as "Learn more" with book icon) + * @param actionText - Optional text for an action link appended to the message + * @param onAction - Optional callback when the action link is clicked * * @example * openSettings()} * /> */ -export const WarningRow: React.FC = ({ title, message, docsURL }) => { +export const WarningRow: React.FC = ({ title, message, docsURL, actionText, onAction }) => { const { t } = useAppTranslation() return ( @@ -48,6 +54,20 @@ export const WarningRow: React.FC = ({ title, message, docsURL

{message} + {actionText && onAction && ( + <> + {" "} + { + e.preventDefault() + onAction() + }}> + {actionText} + + + )}

diff --git a/webview-ui/src/components/chat/__tests__/TooManyToolsWarning.spec.tsx b/webview-ui/src/components/chat/__tests__/TooManyToolsWarning.spec.tsx index dd9c209a092..cb4094f9153 100644 --- a/webview-ui/src/components/chat/__tests__/TooManyToolsWarning.spec.tsx +++ b/webview-ui/src/components/chat/__tests__/TooManyToolsWarning.spec.tsx @@ -1,6 +1,7 @@ -import { render, screen } from "@/utils/test-utils" +import { render, screen, fireEvent } from "@/utils/test-utils" +import { MAX_MCP_TOOLS_THRESHOLD } from "@roo-code/types" -import { TooManyToolsWarning, MAX_MCP_TOOLS_THRESHOLD } from "../TooManyToolsWarning" +import { TooManyToolsWarning } from "../TooManyToolsWarning" // Mock vscode webview messaging vi.mock("@/utils/vscode", () => ({ @@ -36,6 +37,9 @@ vi.mock("@/i18n/TranslationContext", () => ({ if (key === "chat:tooManyTools.messageTemplate") { return `You have ${params?.tools} enabled via ${params?.servers}. Such a high number can confuse the model and lead to errors. Try to keep it below ${params?.threshold}.` } + if (key === "chat:tooManyTools.openMcpSettings") { + return "Open MCP Settings" + } if (key === "chat:apiRequest.errorMessage.docs") { return "Docs" } @@ -255,7 +259,38 @@ describe("TooManyToolsWarning", () => { ).toBeInTheDocument() }) - it("exports MAX_MCP_TOOLS_THRESHOLD constant", () => { - expect(MAX_MCP_TOOLS_THRESHOLD).toBe(40) + it("renders MCP settings link and opens settings when clicked", () => { + const mockWindowPostMessage = vi.spyOn(window, "postMessage") + + // Create more tools than the threshold + const tools = Array.from({ length: MAX_MCP_TOOLS_THRESHOLD + 10 }, (_, i) => ({ + name: `tool${i}`, + enabledForPrompt: true, + })) + + mockMcpServers.mockReturnValue([ + { + name: "server1", + status: "connected", + disabled: false, + tools, + }, + ]) + + render() + + // Verify the link is rendered + const settingsLink = screen.getByText("Open MCP Settings") + expect(settingsLink).toBeInTheDocument() + + // Click the link and verify it posts the message + fireEvent.click(settingsLink) + + expect(mockWindowPostMessage).toHaveBeenCalledWith( + { type: "action", action: "settingsButtonClicked", values: { section: "mcp" } }, + "*", + ) + + mockWindowPostMessage.mockRestore() }) }) diff --git a/webview-ui/src/components/chat/__tests__/WarningRow.spec.tsx b/webview-ui/src/components/chat/__tests__/WarningRow.spec.tsx index 13e4e20f508..38eae810a04 100644 --- a/webview-ui/src/components/chat/__tests__/WarningRow.spec.tsx +++ b/webview-ui/src/components/chat/__tests__/WarningRow.spec.tsx @@ -68,4 +68,42 @@ describe("WarningRow", () => { const warningIcon = container.querySelector("svg") expect(warningIcon).toBeInTheDocument() }) + + it("does not render action link when actionText and onAction are not provided", () => { + render() + + expect(screen.queryByText("Open Settings")).not.toBeInTheDocument() + }) + + it("renders action link when actionText and onAction are provided", () => { + const mockOnAction = vi.fn() + render( + , + ) + + const actionLink = screen.getByText("Open Settings") + expect(actionLink).toBeInTheDocument() + }) + + it("calls onAction when action link is clicked", () => { + const mockOnAction = vi.fn() + render( + , + ) + + const actionLink = screen.getByText("Open Settings") + fireEvent.click(actionLink) + + expect(mockOnAction).toHaveBeenCalledTimes(1) + }) }) diff --git a/webview-ui/src/i18n/locales/ca/chat.json b/webview-ui/src/i18n/locales/ca/chat.json index ac46cea9f2f..c989cc030bd 100644 --- a/webview-ui/src/i18n/locales/ca/chat.json +++ b/webview-ui/src/i18n/locales/ca/chat.json @@ -499,6 +499,7 @@ "toolsPart_other": "{{count}} eines", "serversPart_one": "{{count}} servidor MCP", "serversPart_other": "{{count}} servidors MCP", - "messageTemplate": "Tens {{tools}} habilitades via {{servers}}. Un nombre tant alt pot confondre el model i portar a errors. Intenta mantenir-lo per sota de {{threshold}}." + "messageTemplate": "Tens {{tools}} habilitades via {{servers}}. Un nombre tant alt pot confondre el model i portar a errors. Intenta mantenir-lo per sota de {{threshold}}.", + "openMcpSettings": "Obrir configuració de MCP" } } diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index e5d7e925407..254052278bd 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -499,6 +499,7 @@ "toolsPart_other": "{{count}} Tools", "serversPart_one": "{{count}} MCP-Server", "serversPart_other": "{{count}} MCP-Server", - "messageTemplate": "Du hast {{tools}} über {{servers}} aktiviert. Eine so hohe Anzahl kann das Modell verwirren und zu Fehlern führen. Versuche, es unter {{threshold}} zu halten." + "messageTemplate": "Du hast {{tools}} über {{servers}} aktiviert. Eine so hohe Anzahl kann das Modell verwirren und zu Fehlern führen. Versuche, es unter {{threshold}} zu halten.", + "openMcpSettings": "MCP-Einstellungen öffnen" } } diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index 1c74d4bbe75..d92957916b3 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -491,6 +491,7 @@ "toolsPart_other": "{{count}} tools", "serversPart_one": "{{count}} MCP server", "serversPart_other": "{{count}} MCP servers", - "messageTemplate": "You have {{tools}} enabled via {{servers}}. Such a high number can confuse the model and lead to errors. Try to keep it below {{threshold}}." + "messageTemplate": "You have {{tools}} enabled via {{servers}}. Such a high number can confuse the model and lead to errors. Try to keep it below {{threshold}}.", + "openMcpSettings": "Open MCP Settings" } } diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index 1df3bcd80f5..ae0d02232f6 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -499,6 +499,7 @@ "toolsPart_other": "{{count}} herramientas", "serversPart_one": "{{count}} servidor MCP", "serversPart_other": "{{count}} servidores MCP", - "messageTemplate": "Tienes {{tools}} habilitadas a través de {{servers}}. Un número tan alto puede confundir al modelo y llevar a errores. Intenta mantenerlo por debajo de {{threshold}}." + "messageTemplate": "Tienes {{tools}} habilitadas a través de {{servers}}. Un número tan alto puede confundir al modelo y llevar a errores. Intenta mantenerlo por debajo de {{threshold}}.", + "openMcpSettings": "Abrir configuración de MCP" } } diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index c3d36d96c95..56eec51e96b 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -499,6 +499,7 @@ "toolsPart_other": "{{count}} outils", "serversPart_one": "{{count}} serveur MCP", "serversPart_other": "{{count}} serveurs MCP", - "messageTemplate": "Tu as {{tools}} activés via {{servers}}. Un nombre aussi élevé peut confondre le modèle et entraîner des erreurs. Essaie de le maintenir en dessous de {{threshold}}." + "messageTemplate": "Tu as {{tools}} activés via {{servers}}. Un nombre aussi élevé peut confondre le modèle et entraîner des erreurs. Essaie de le maintenir en dessous de {{threshold}}.", + "openMcpSettings": "Ouvrir les paramètres MCP" } } diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json index 545aefc8ae6..a58e3abff44 100644 --- a/webview-ui/src/i18n/locales/hi/chat.json +++ b/webview-ui/src/i18n/locales/hi/chat.json @@ -499,6 +499,7 @@ "toolsPart_other": "{{count}} उपकरण", "serversPart_one": "{{count}} MCP सर्वर", "serversPart_other": "{{count}} MCP सर्वर", - "messageTemplate": "आपके पास {{servers}} के माध्यम से {{tools}} सक्षम हैं। इतनी अधिक संख्या मॉडल को भ्रमित कर सकती है और त्रुटियों का कारण बन सकती है। इसे {{threshold}} से नीचे रखने का प्रयास करें।" + "messageTemplate": "आपके पास {{servers}} के माध्यम से {{tools}} सक्षम हैं। इतनी अधिक संख्या मॉडल को भ्रमित कर सकती है और त्रुटियों का कारण बन सकती है। इसे {{threshold}} से नीचे रखने का प्रयास करें।", + "openMcpSettings": "MCP सेटिंग्स खोलें" } } diff --git a/webview-ui/src/i18n/locales/id/chat.json b/webview-ui/src/i18n/locales/id/chat.json index c341f7a92fa..c00a6d5ab3b 100644 --- a/webview-ui/src/i18n/locales/id/chat.json +++ b/webview-ui/src/i18n/locales/id/chat.json @@ -505,6 +505,7 @@ "toolsPart_other": "{{count}} alat", "serversPart_one": "{{count}} server MCP", "serversPart_other": "{{count}} server MCP", - "messageTemplate": "Anda memiliki {{tools}} diaktifkan melalui {{servers}}. Jumlah yang begitu besar dapat membingungkan model dan menyebabkan kesalahan. Cobalah untuk menjaganya di bawah {{threshold}}." + "messageTemplate": "Anda memiliki {{tools}} diaktifkan melalui {{servers}}. Jumlah yang begitu besar dapat membingungkan model dan menyebabkan kesalahan. Cobalah untuk menjaganya di bawah {{threshold}}.", + "openMcpSettings": "Buka Pengaturan MCP" } } diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index c841db324e6..3fe64691356 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -499,6 +499,7 @@ "toolsPart_other": "{{count}} strumenti", "serversPart_one": "{{count}} server MCP", "serversPart_other": "{{count}} server MCP", - "messageTemplate": "Hai {{tools}} abilitate via {{servers}}. Un numero così alto può confondere il modello e portare a errori. Prova a mantenerlo sotto {{threshold}}." + "messageTemplate": "Hai {{tools}} abilitate via {{servers}}. Un numero così alto può confondere il modello e portare a errori. Prova a mantenerlo sotto {{threshold}}.", + "openMcpSettings": "Apri impostazioni MCP" } } diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json index 6eb8d62eb35..68f3597396c 100644 --- a/webview-ui/src/i18n/locales/ja/chat.json +++ b/webview-ui/src/i18n/locales/ja/chat.json @@ -499,6 +499,7 @@ "toolsPart_other": "{{count}} ツール", "serversPart_one": "{{count}} MCP サーバー", "serversPart_other": "{{count}} MCP サーバー", - "messageTemplate": "{{servers}}経由で{{tools}}が有効になっています。このような高い数は、モデルを混乱させてエラーを引き起こす可能性があります。{{threshold}}以下に保つようにしてください。" + "messageTemplate": "{{servers}}経由で{{tools}}が有効になっています。このような高い数は、モデルを混乱させてエラーを引き起こす可能性があります。{{threshold}}以下に保つようにしてください。", + "openMcpSettings": "MCP 設定を開く" } } diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json index 8bf97b87692..a9934a4fd3d 100644 --- a/webview-ui/src/i18n/locales/ko/chat.json +++ b/webview-ui/src/i18n/locales/ko/chat.json @@ -499,6 +499,7 @@ "toolsPart_other": "{{count}}개 도구", "serversPart_one": "{{count}}개 MCP 서버", "serversPart_other": "{{count}}개 MCP 서버", - "messageTemplate": "{{servers}}를 통해 {{tools}}가 활성화되어 있습니다. 이렇게 많은 수의 도구는 모델을 혼동시키고 오류를 유발할 수 있습니다. {{threshold}} 이하로 유지하도록 노력하세요." + "messageTemplate": "{{servers}}를 통해 {{tools}}가 활성화되어 있습니다. 이렇게 많은 수의 도구는 모델을 혼동시키고 오류를 유발할 수 있습니다. {{threshold}} 이하로 유지하도록 노력하세요.", + "openMcpSettings": "MCP 설정 열기" } } diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json index 3c56682d873..d1747ed529d 100644 --- a/webview-ui/src/i18n/locales/nl/chat.json +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -499,6 +499,7 @@ "toolsPart_other": "{{count}} tools", "serversPart_one": "{{count}} MCP server", "serversPart_other": "{{count}} MCP servers", - "messageTemplate": "Je hebt {{tools}} ingeschakeld via {{servers}}. Zoveel tools kunnen het model verwarren en tot fouten leiden. Probeer dit onder {{threshold}} te houden." + "messageTemplate": "Je hebt {{tools}} ingeschakeld via {{servers}}. Zoveel tools kunnen het model verwarren en tot fouten leiden. Probeer dit onder {{threshold}} te houden.", + "openMcpSettings": "MCP-instellingen openen" } } diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index d9f8a976dab..acdd23f06be 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -499,6 +499,7 @@ "toolsPart_other": "{{count}} narzędzi", "serversPart_one": "{{count}} serwer MCP", "serversPart_other": "{{count}} serwerów MCP", - "messageTemplate": "Masz {{tools}} włączonych przez {{servers}}. Taka duża liczba może zamieszać model i prowadzić do błędów. Staraj się, aby była poniżej {{threshold}}." + "messageTemplate": "Masz {{tools}} włączonych przez {{servers}}. Taka duża liczba może zamieszać model i prowadzić do błędów. Staraj się, aby była poniżej {{threshold}}.", + "openMcpSettings": "Otwórz ustawienia MCP" } } diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index 57147536594..30a86559fa4 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -499,6 +499,7 @@ "toolsPart_other": "{{count}} ferramentas", "serversPart_one": "{{count}} servidor MCP", "serversPart_other": "{{count}} servidores MCP", - "messageTemplate": "Você tem {{tools}} habilitadas via {{servers}}. Um número tão alto pode confundir o modelo e levar a erros. Tente mantê-lo abaixo de {{threshold}}." + "messageTemplate": "Você tem {{tools}} habilitadas via {{servers}}. Um número tão alto pode confundir o modelo e levar a erros. Tente mantê-lo abaixo de {{threshold}}.", + "openMcpSettings": "Abrir Configurações MCP" } } diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index 97bd54c638b..008de4397d9 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -500,6 +500,7 @@ "toolsPart_other": "{{count}} инструментов", "serversPart_one": "{{count}} сервер MCP", "serversPart_other": "{{count}} серверов MCP", - "messageTemplate": "У тебя включено {{tools}} через {{servers}}. Такое большое количество может сбить модель с толку и привести к ошибкам. Постарайся держать это ниже {{threshold}}." + "messageTemplate": "У тебя включено {{tools}} через {{servers}}. Такое большое количество может сбить модель с толку и привести к ошибкам. Постарайся держать это ниже {{threshold}}.", + "openMcpSettings": "Открыть настройки MCP" } } diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index 93f1e6572ea..f88ed480c0b 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -500,6 +500,7 @@ "toolsPart_other": "{{count}} araç", "serversPart_one": "{{count}} MCP sunucusu", "serversPart_other": "{{count}} MCP sunucusu", - "messageTemplate": "{{servers}} üzerinden {{tools}} etkinleştirilmiş durumda. Bu kadar fazlası modeli kafası karışabilir ve hatalara neden olabilir. {{threshold}} altında tutmaya çalış." + "messageTemplate": "{{servers}} üzerinden {{tools}} etkinleştirilmiş durumda. Bu kadar fazlası modeli kafası karışabilir ve hatalara neden olabilir. {{threshold}} altında tutmaya çalış.", + "openMcpSettings": "MCP Ayarlarını Aç" } } diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index 551672f37a4..c5724152f88 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -500,6 +500,7 @@ "toolsPart_other": "{{count}} công cụ", "serversPart_one": "{{count}} máy chủ MCP", "serversPart_other": "{{count}} máy chủ MCP", - "messageTemplate": "Bạn đã bật {{tools}} qua {{servers}}. Số lượng lớn như vậy có thể khiến mô hình bối rối và dẫn đến lỗi. Cố gắng giữ nó dưới {{threshold}}." + "messageTemplate": "Bạn đã bật {{tools}} qua {{servers}}. Số lượng lớn như vậy có thể khiến mô hình bối rối và dẫn đến lỗi. Cố gắng giữ nó dưới {{threshold}}.", + "openMcpSettings": "Mở cài đặt MCP" } } diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index fdb1b8c92e0..e13124379e7 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -500,6 +500,7 @@ "toolsPart_other": "{{count}} 个工具", "serversPart_one": "{{count}} 个 MCP 服务", "serversPart_other": "{{count}} 个 MCP 服务", - "messageTemplate": "你通过 {{servers}} 启用了 {{tools}}。这么多数量会混淆模型并导致错误。建议将其保持在 {{threshold}} 以下。" + "messageTemplate": "你通过 {{servers}} 启用了 {{tools}}。这么多数量会混淆模型并导致错误。建议将其保持在 {{threshold}} 以下。", + "openMcpSettings": "打开 MCP 设置" } } diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index 92f210f96f9..5d20352c573 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -500,6 +500,7 @@ "toolsPart_other": "{{count}} 個工具", "serversPart_one": "{{count}} 個 MCP 伺服器", "serversPart_other": "{{count}} 個 MCP 伺服器", - "messageTemplate": "你已啟用 {{tools}}(透過 {{servers}})。這麼多的工具可能會混淆模型並導致錯誤。請嘗試保持在 {{threshold}} 以下。" + "messageTemplate": "你已啟用 {{tools}}(透過 {{servers}})。這麼多的工具可能會混淆模型並導致錯誤。請嘗試保持在 {{threshold}} 以下。", + "openMcpSettings": "開啟 MCP 設定" } } From 2a224a025eae3de186c3080ff47ff486c040aaf1 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Sun, 18 Jan 2026 08:39:58 -0500 Subject: [PATCH 6/9] Add to MCP settings too --- .../components/chat/TooManyToolsWarning.tsx | 46 ++--------- webview-ui/src/components/mcp/McpView.tsx | 27 ++++++ webview-ui/src/hooks/useTooManyTools.ts | 82 +++++++++++++++++++ 3 files changed, 114 insertions(+), 41 deletions(-) create mode 100644 webview-ui/src/hooks/useTooManyTools.ts diff --git a/webview-ui/src/components/chat/TooManyToolsWarning.tsx b/webview-ui/src/components/chat/TooManyToolsWarning.tsx index 4afffa354c2..697fad19ae4 100644 --- a/webview-ui/src/components/chat/TooManyToolsWarning.tsx +++ b/webview-ui/src/components/chat/TooManyToolsWarning.tsx @@ -1,7 +1,6 @@ -import React, { useCallback, useMemo } from "react" +import React, { useCallback } from "react" import { useAppTranslation } from "@/i18n/TranslationContext" -import { useExtensionState } from "@src/context/ExtensionStateContext" -import { MAX_MCP_TOOLS_THRESHOLD } from "@roo-code/types" +import { useTooManyTools } from "@src/hooks/useTooManyTools" import WarningRow from "./WarningRow" /** @@ -16,55 +15,20 @@ import WarningRow from "./WarningRow" */ export const TooManyToolsWarning: React.FC = () => { const { t } = useAppTranslation() - const { mcpServers } = useExtensionState() - - const { enabledServerCount, enabledToolCount } = useMemo(() => { - let serverCount = 0 - let toolCount = 0 - - for (const server of mcpServers) { - // Skip disabled servers - if (server.disabled) continue - - // Skip servers that are not connected - if (server.status !== "connected") continue - - serverCount++ - - // Count enabled tools on this server - if (server.tools) { - for (const tool of server.tools) { - // Tool is enabled if enabledForPrompt is undefined (default) or true - if (tool.enabledForPrompt !== false) { - toolCount++ - } - } - } - } - - return { enabledServerCount: serverCount, enabledToolCount: toolCount } - }, [mcpServers]) + const { isOverThreshold, title, message } = useTooManyTools() const handleOpenMcpSettings = useCallback(() => { window.postMessage({ type: "action", action: "settingsButtonClicked", values: { section: "mcp" } }, "*") }, []) // Don't show warning if under threshold - if (enabledToolCount <= MAX_MCP_TOOLS_THRESHOLD) { + if (!isOverThreshold) { return null } - const toolsPart = t("chat:tooManyTools.toolsPart", { count: enabledToolCount }) - const serversPart = t("chat:tooManyTools.serversPart", { count: enabledServerCount }) - const message = t("chat:tooManyTools.messageTemplate", { - tools: toolsPart, - servers: serversPart, - threshold: MAX_MCP_TOOLS_THRESHOLD, - }) - return ( { } = useExtensionState() const { t } = useAppTranslation() + const { isOverThreshold, title, message } = useTooManyTools() return (
@@ -99,6 +101,31 @@ const McpView = () => {
+ {/* Too Many Tools Warning */} + {isOverThreshold && ( +
+
+ + {title} +
+
+ {message} +
+
+ )} + {/* Server List */} {servers.length > 0 && (
diff --git a/webview-ui/src/hooks/useTooManyTools.ts b/webview-ui/src/hooks/useTooManyTools.ts new file mode 100644 index 00000000000..75bfe226efd --- /dev/null +++ b/webview-ui/src/hooks/useTooManyTools.ts @@ -0,0 +1,82 @@ +import { useMemo } from "react" +import { useExtensionState } from "@src/context/ExtensionStateContext" +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { MAX_MCP_TOOLS_THRESHOLD } from "@roo-code/types" + +export interface TooManyToolsInfo { + /** Number of enabled and connected MCP servers */ + enabledServerCount: number + /** Total number of enabled tools across all enabled servers */ + enabledToolCount: number + /** Whether the tool count exceeds the threshold */ + isOverThreshold: boolean + /** The maximum recommended threshold */ + threshold: number + /** Localized title string */ + title: string + /** Localized message string */ + message: string +} + +/** + * Hook that calculates tool counts and provides localized warning messages. + * Used by TooManyToolsWarning components in both chat and MCP settings views. + * + * @returns Tool count information and localized messages + * + * @example + * const { isOverThreshold, title, message } = useTooManyTools() + * if (isOverThreshold) { + * // Show warning + * } + */ +export function useTooManyTools(): TooManyToolsInfo { + const { t } = useAppTranslation() + const { mcpServers } = useExtensionState() + + const { enabledServerCount, enabledToolCount } = useMemo(() => { + let serverCount = 0 + let toolCount = 0 + + for (const server of mcpServers) { + // Skip disabled servers + if (server.disabled) continue + + // Skip servers that are not connected + if (server.status !== "connected") continue + + serverCount++ + + // Count enabled tools on this server + if (server.tools) { + for (const tool of server.tools) { + // Tool is enabled if enabledForPrompt is undefined (default) or true + if (tool.enabledForPrompt !== false) { + toolCount++ + } + } + } + } + + return { enabledServerCount: serverCount, enabledToolCount: toolCount } + }, [mcpServers]) + + const isOverThreshold = enabledToolCount > MAX_MCP_TOOLS_THRESHOLD + + const toolsPart = t("chat:tooManyTools.toolsPart", { count: enabledToolCount }) + const serversPart = t("chat:tooManyTools.serversPart", { count: enabledServerCount }) + const message = t("chat:tooManyTools.messageTemplate", { + tools: toolsPart, + servers: serversPart, + threshold: MAX_MCP_TOOLS_THRESHOLD, + }) + + return { + enabledServerCount, + enabledToolCount, + isOverThreshold, + threshold: MAX_MCP_TOOLS_THRESHOLD, + title: t("chat:tooManyTools.title"), + message, + } +} From cd44d1346a513cc5768726df14c1290f3c2d3b7d Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Sun, 18 Jan 2026 08:40:31 -0500 Subject: [PATCH 7/9] Bump max tools up to 60 since github itself has 50+ --- packages/types/src/mcp.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/types/src/mcp.ts b/packages/types/src/mcp.ts index fe725934c24..70e29e87d3e 100644 --- a/packages/types/src/mcp.ts +++ b/packages/types/src/mcp.ts @@ -4,7 +4,7 @@ import { z } from "zod" * Maximum number of MCP tools that can be enabled before showing a warning. * LLMs tend to perform poorly when given too many tools to choose from. */ -export const MAX_MCP_TOOLS_THRESHOLD = 40 +export const MAX_MCP_TOOLS_THRESHOLD = 60 /** * McpServerUse From c786a82713a434883847dde3bb3ae541e08760bf Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Sun, 18 Jan 2026 08:49:13 -0500 Subject: [PATCH 8/9] DRY --- packages/types/src/mcp.ts | 50 +++++++++++++++++++++++++ src/core/task/Task.ts | 35 ++++------------- webview-ui/src/hooks/useTooManyTools.ts | 29 +------------- 3 files changed, 59 insertions(+), 55 deletions(-) diff --git a/packages/types/src/mcp.ts b/packages/types/src/mcp.ts index 70e29e87d3e..f1bfde325de 100644 --- a/packages/types/src/mcp.ts +++ b/packages/types/src/mcp.ts @@ -134,3 +134,53 @@ export type McpErrorEntry = { timestamp: number level: "error" | "warn" | "info" } + +/** + * Result of counting enabled MCP tools across servers. + */ +export interface EnabledMcpToolsCount { + /** Number of enabled and connected MCP servers */ + enabledServerCount: number + /** Total number of enabled tools across all enabled servers */ + enabledToolCount: number +} + +/** + * Count the number of enabled MCP tools across all enabled and connected servers. + * This is a pure function that can be used in both backend and frontend contexts. + * + * @param servers - Array of MCP server objects + * @returns Object with enabledToolCount and enabledServerCount + * + * @example + * const { enabledToolCount, enabledServerCount } = countEnabledMcpTools(mcpServers) + * if (enabledToolCount > MAX_MCP_TOOLS_THRESHOLD) { + * // Show warning + * } + */ +export function countEnabledMcpTools(servers: McpServer[]): EnabledMcpToolsCount { + let serverCount = 0 + let toolCount = 0 + + for (const server of servers) { + // Skip disabled servers + if (server.disabled) continue + + // Skip servers that are not connected + if (server.status !== "connected") continue + + serverCount++ + + // Count enabled tools on this server + if (server.tools) { + for (const tool of server.tools) { + // Tool is enabled if enabledForPrompt is undefined (default) or true + if (tool.enabledForPrompt !== false) { + toolCount++ + } + } + } + } + + return { enabledToolCount: toolCount, enabledServerCount: serverCount } +} diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 5ec26d5ba2f..e933fbff2cd 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -54,6 +54,7 @@ import { TOOL_PROTOCOL, ConsecutiveMistakeError, MAX_MCP_TOOLS_THRESHOLD, + countEnabledMcpTools, } from "@roo-code/types" import { TelemetryService } from "@roo-code/telemetry" import { CloudService, BridgeOrchestrator } from "@roo-code/cloud" @@ -1834,15 +1835,12 @@ export class Task extends EventEmitter implements TaskLike { // Start / Resume / Abort / Dispose /** - * Count the number of enabled MCP tools across all enabled and connected servers. + * Get enabled MCP tools count for this task. * Returns the count along with the number of servers contributing. * * @returns Object with enabledToolCount and enabledServerCount */ - private async countEnabledMcpTools(): Promise<{ enabledToolCount: number; enabledServerCount: number }> { - let serverCount = 0 - let toolCount = 0 - + private async getEnabledMcpToolsCount(): Promise<{ enabledToolCount: number; enabledServerCount: number }> { try { const provider = this.providerRef.deref() if (!provider) { @@ -1860,30 +1858,11 @@ export class Task extends EventEmitter implements TaskLike { } const servers = mcpHub.getServers() - for (const server of servers) { - // Skip disabled servers - if (server.disabled) continue - - // Skip servers that are not connected - if (server.status !== "connected") continue - - serverCount++ - - // Count enabled tools on this server - if (server.tools) { - for (const tool of server.tools) { - // Tool is enabled if enabledForPrompt is undefined (default) or true - if (tool.enabledForPrompt !== false) { - toolCount++ - } - } - } - } + return countEnabledMcpTools(servers) } catch (error) { - console.error("[Task#countEnabledMcpTools] Error counting MCP tools:", error) + console.error("[Task#getEnabledMcpToolsCount] Error counting MCP tools:", error) + return { enabledToolCount: 0, enabledServerCount: 0 } } - - return { enabledToolCount: toolCount, enabledServerCount: serverCount } } private async startTask(task?: string, images?: string[]): Promise { @@ -1914,7 +1893,7 @@ export class Task extends EventEmitter implements TaskLike { await this.say("text", task, images) // Check for too many MCP tools and warn the user - const { enabledToolCount, enabledServerCount } = await this.countEnabledMcpTools() + const { enabledToolCount, enabledServerCount } = await this.getEnabledMcpToolsCount() if (enabledToolCount > MAX_MCP_TOOLS_THRESHOLD) { await this.say( "too_many_tools_warning", diff --git a/webview-ui/src/hooks/useTooManyTools.ts b/webview-ui/src/hooks/useTooManyTools.ts index 75bfe226efd..ba18b10a708 100644 --- a/webview-ui/src/hooks/useTooManyTools.ts +++ b/webview-ui/src/hooks/useTooManyTools.ts @@ -1,7 +1,7 @@ import { useMemo } from "react" import { useExtensionState } from "@src/context/ExtensionStateContext" import { useAppTranslation } from "@src/i18n/TranslationContext" -import { MAX_MCP_TOOLS_THRESHOLD } from "@roo-code/types" +import { MAX_MCP_TOOLS_THRESHOLD, countEnabledMcpTools } from "@roo-code/types" export interface TooManyToolsInfo { /** Number of enabled and connected MCP servers */ @@ -34,32 +34,7 @@ export function useTooManyTools(): TooManyToolsInfo { const { t } = useAppTranslation() const { mcpServers } = useExtensionState() - const { enabledServerCount, enabledToolCount } = useMemo(() => { - let serverCount = 0 - let toolCount = 0 - - for (const server of mcpServers) { - // Skip disabled servers - if (server.disabled) continue - - // Skip servers that are not connected - if (server.status !== "connected") continue - - serverCount++ - - // Count enabled tools on this server - if (server.tools) { - for (const tool of server.tools) { - // Tool is enabled if enabledForPrompt is undefined (default) or true - if (tool.enabledForPrompt !== false) { - toolCount++ - } - } - } - } - - return { enabledServerCount: serverCount, enabledToolCount: toolCount } - }, [mcpServers]) + const { enabledServerCount, enabledToolCount } = useMemo(() => countEnabledMcpTools(mcpServers), [mcpServers]) const isOverThreshold = enabledToolCount > MAX_MCP_TOOLS_THRESHOLD From 4ae60ce94f146a3c74e5d16fb478225308d83c11 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Sun, 18 Jan 2026 09:03:51 -0500 Subject: [PATCH 9/9] Fix test --- .../chat/__tests__/TooManyToolsWarning.spec.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/webview-ui/src/components/chat/__tests__/TooManyToolsWarning.spec.tsx b/webview-ui/src/components/chat/__tests__/TooManyToolsWarning.spec.tsx index cb4094f9153..85560201da9 100644 --- a/webview-ui/src/components/chat/__tests__/TooManyToolsWarning.spec.tsx +++ b/webview-ui/src/components/chat/__tests__/TooManyToolsWarning.spec.tsx @@ -224,11 +224,11 @@ describe("TooManyToolsWarning", () => { it("counts tools across multiple servers", () => { // Create tools across multiple servers - const tools1 = Array.from({ length: 25 }, (_, i) => ({ + const tools1 = Array.from({ length: 35 }, (_, i) => ({ name: `server1tool${i}`, enabledForPrompt: true, })) - const tools2 = Array.from({ length: 20 }, (_, i) => ({ + const tools2 = Array.from({ length: 30 }, (_, i) => ({ name: `server2tool${i}`, enabledForPrompt: true, })) @@ -250,11 +250,11 @@ describe("TooManyToolsWarning", () => { render() - // 25 + 20 = 45 tools > 40 threshold + // 35 + 30 = 65 tools > 60 threshold expect(screen.getByText("Too many tools enabled")).toBeInTheDocument() expect( screen.getByText( - `You have 45 tools enabled via 2 MCP servers. Such a high number can confuse the model and lead to errors. Try to keep it below ${MAX_MCP_TOOLS_THRESHOLD}.`, + `You have 65 tools enabled via 2 MCP servers. Such a high number can confuse the model and lead to errors. Try to keep it below ${MAX_MCP_TOOLS_THRESHOLD}.`, ), ).toBeInTheDocument() })