Browse Source

fix: validate MCP tool exists before execution (#7632)

Co-authored-by: Roo Code <[email protected]>
Co-authored-by: Daniel Riccio <[email protected]>
roomote[bot] 5 months ago
parent
commit
7935c94827

+ 10 - 0
src/core/prompts/responses.ts

@@ -72,6 +72,16 @@ Otherwise, if you have not completed the task and do not need additional informa
 	invalidMcpToolArgumentError: (serverName: string, toolName: string) =>
 		`Invalid JSON argument used with ${serverName} for ${toolName}. Please retry with a properly formatted JSON argument.`,
 
+	unknownMcpToolError: (serverName: string, toolName: string, availableTools: string[]) => {
+		const toolsList = availableTools.length > 0 ? availableTools.join(", ") : "No tools available"
+		return `Tool '${toolName}' does not exist on server '${serverName}'.\n\nAvailable tools on this server: ${toolsList}\n\nPlease use one of the available tools or check if the server is properly configured.`
+	},
+
+	unknownMcpServerError: (serverName: string, availableServers: string[]) => {
+		const serversList = availableServers.length > 0 ? availableServers.join(", ") : "No servers available"
+		return `Server '${serverName}' is not configured. Available servers: ${serversList}`
+	},
+
 	toolResult: (
 		text: string,
 		images?: string[],

+ 266 - 0
src/core/tools/__tests__/useMcpToolTool.spec.ts

@@ -10,6 +10,14 @@ vi.mock("../../prompts/responses", () => ({
 		toolResult: vi.fn((result: string) => `Tool result: ${result}`),
 		toolError: vi.fn((error: string) => `Tool error: ${error}`),
 		invalidMcpToolArgumentError: vi.fn((server: string, tool: string) => `Invalid args for ${server}:${tool}`),
+		unknownMcpToolError: vi.fn((server: string, tool: string, availableTools: string[]) => {
+			const toolsList = availableTools.length > 0 ? availableTools.join(", ") : "No tools available"
+			return `Tool '${tool}' does not exist on server '${server}'. Available tools: ${toolsList}`
+		}),
+		unknownMcpServerError: vi.fn((server: string, availableServers: string[]) => {
+			const list = availableServers.length > 0 ? availableServers.join(", ") : "No servers available"
+			return `Server '${server}' is not configured. Available servers: ${list}`
+		}),
 	},
 }))
 
@@ -18,6 +26,12 @@ vi.mock("../../../i18n", () => ({
 		if (key === "mcp:errors.invalidJsonArgument" && params?.toolName) {
 			return `Roo tried to use ${params.toolName} with an invalid JSON argument. Retrying...`
 		}
+		if (key === "mcp:errors.toolNotFound" && params) {
+			return `Tool '${params.toolName}' does not exist on server '${params.serverName}'. Available tools: ${params.availableTools}`
+		}
+		if (key === "mcp:errors.serverNotFound" && params) {
+			return `MCP server '${params.serverName}' is not configured. Available servers: ${params.availableServers}`
+		}
 		return key
 	}),
 }))
@@ -40,6 +54,7 @@ describe("useMcpToolTool", () => {
 			deref: vi.fn().mockReturnValue({
 				getMcpHub: vi.fn().mockReturnValue({
 					callTool: vi.fn(),
+					getAllServers: vi.fn().mockReturnValue([]),
 				}),
 				postMessageToWebview: vi.fn(),
 			}),
@@ -224,6 +239,10 @@ describe("useMcpToolTool", () => {
 				partial: false,
 			}
 
+			// Ensure validation does not fail due to unknown server by returning no provider once
+			// This makes validateToolExists return isValid: true and proceed to askApproval
+			mockProviderRef.deref.mockReturnValueOnce(undefined as any)
+
 			mockAskApproval.mockResolvedValue(false)
 
 			await useMcpToolTool(
@@ -252,6 +271,19 @@ describe("useMcpToolTool", () => {
 				partial: false,
 			}
 
+			// Ensure validation passes so askApproval is reached and throws
+			mockProviderRef.deref.mockReturnValueOnce({
+				getMcpHub: () => ({
+					getAllServers: vi
+						.fn()
+						.mockReturnValue([
+							{ name: "test_server", tools: [{ name: "test_tool", description: "desc" }] },
+						]),
+					callTool: vi.fn(),
+				}),
+				postMessageToWebview: vi.fn(),
+			})
+
 			const error = new Error("Unexpected error")
 			mockAskApproval.mockRejectedValue(error)
 
@@ -266,5 +298,239 @@ describe("useMcpToolTool", () => {
 
 			expect(mockHandleError).toHaveBeenCalledWith("executing MCP tool", error)
 		})
+
+		it("should reject unknown tool names", async () => {
+			// Reset consecutiveMistakeCount for this test
+			mockTask.consecutiveMistakeCount = 0
+
+			const mockServers = [
+				{
+					name: "test-server",
+					tools: [
+						{ name: "existing-tool-1", description: "Tool 1" },
+						{ name: "existing-tool-2", description: "Tool 2" },
+					],
+				},
+			]
+
+			mockProviderRef.deref.mockReturnValue({
+				getMcpHub: () => ({
+					getAllServers: vi.fn().mockReturnValue(mockServers),
+					callTool: vi.fn(),
+				}),
+				postMessageToWebview: vi.fn(),
+			})
+
+			const block: ToolUse = {
+				type: "tool_use",
+				name: "use_mcp_tool",
+				params: {
+					server_name: "test-server",
+					tool_name: "non-existing-tool",
+					arguments: JSON.stringify({ test: "data" }),
+				},
+				partial: false,
+			}
+
+			await useMcpToolTool(
+				mockTask as Task,
+				block,
+				mockAskApproval,
+				mockHandleError,
+				mockPushToolResult,
+				mockRemoveClosingTag,
+			)
+
+			expect(mockTask.consecutiveMistakeCount).toBe(1)
+			expect(mockTask.recordToolError).toHaveBeenCalledWith("use_mcp_tool")
+			expect(mockTask.say).toHaveBeenCalledWith("error", expect.stringContaining("does not exist"))
+			// Check that the error message contains available tools
+			expect(mockPushToolResult).toHaveBeenCalledWith(expect.stringContaining("existing-tool-1"))
+			expect(mockPushToolResult).toHaveBeenCalledWith(expect.stringContaining("existing-tool-2"))
+		})
+
+		it("should handle server with no tools", async () => {
+			// Reset consecutiveMistakeCount for this test
+			mockTask.consecutiveMistakeCount = 0
+
+			const mockServers = [
+				{
+					name: "test-server",
+					tools: [],
+				},
+			]
+
+			mockProviderRef.deref.mockReturnValue({
+				getMcpHub: () => ({
+					getAllServers: vi.fn().mockReturnValue(mockServers),
+					callTool: vi.fn(),
+				}),
+				postMessageToWebview: vi.fn(),
+			})
+
+			const block: ToolUse = {
+				type: "tool_use",
+				name: "use_mcp_tool",
+				params: {
+					server_name: "test-server",
+					tool_name: "any-tool",
+					arguments: JSON.stringify({ test: "data" }),
+				},
+				partial: false,
+			}
+
+			await useMcpToolTool(
+				mockTask as Task,
+				block,
+				mockAskApproval,
+				mockHandleError,
+				mockPushToolResult,
+				mockRemoveClosingTag,
+			)
+
+			expect(mockTask.consecutiveMistakeCount).toBe(1)
+			expect(mockTask.recordToolError).toHaveBeenCalledWith("use_mcp_tool")
+			expect(mockTask.say).toHaveBeenCalledWith("error", expect.stringContaining("does not exist"))
+			expect(mockPushToolResult).toHaveBeenCalledWith(expect.stringContaining("No tools available"))
+		})
+
+		it("should allow valid tool names", async () => {
+			// Reset consecutiveMistakeCount for this test
+			mockTask.consecutiveMistakeCount = 0
+
+			const mockServers = [
+				{
+					name: "test-server",
+					tools: [{ name: "valid-tool", description: "Valid Tool" }],
+				},
+			]
+
+			const mockToolResult = {
+				content: [{ type: "text", text: "Tool executed successfully" }],
+			}
+
+			mockProviderRef.deref.mockReturnValue({
+				getMcpHub: () => ({
+					getAllServers: vi.fn().mockReturnValue(mockServers),
+					callTool: vi.fn().mockResolvedValue(mockToolResult),
+				}),
+				postMessageToWebview: vi.fn(),
+			})
+
+			const block: ToolUse = {
+				type: "tool_use",
+				name: "use_mcp_tool",
+				params: {
+					server_name: "test-server",
+					tool_name: "valid-tool",
+					arguments: JSON.stringify({ test: "data" }),
+				},
+				partial: false,
+			}
+
+			mockAskApproval.mockResolvedValue(true)
+
+			await useMcpToolTool(
+				mockTask as Task,
+				block,
+				mockAskApproval,
+				mockHandleError,
+				mockPushToolResult,
+				mockRemoveClosingTag,
+			)
+
+			expect(mockTask.consecutiveMistakeCount).toBe(0)
+			expect(mockTask.recordToolError).not.toHaveBeenCalled()
+			expect(mockTask.say).toHaveBeenCalledWith("mcp_server_request_started")
+			expect(mockTask.say).toHaveBeenCalledWith("mcp_server_response", "Tool executed successfully")
+		})
+
+		it("should reject unknown server names with available servers listed", async () => {
+			// Arrange
+			mockTask.consecutiveMistakeCount = 0
+
+			const mockServers = [{ name: "s1", tools: [] }]
+			const callToolMock = vi.fn()
+
+			mockProviderRef.deref.mockReturnValue({
+				getMcpHub: () => ({
+					getAllServers: vi.fn().mockReturnValue(mockServers),
+					callTool: callToolMock,
+				}),
+				postMessageToWebview: vi.fn(),
+			})
+
+			const block: ToolUse = {
+				type: "tool_use",
+				name: "use_mcp_tool",
+				params: {
+					server_name: "unknown",
+					tool_name: "any-tool",
+					arguments: "{}",
+				},
+				partial: false,
+			}
+
+			// Act
+			await useMcpToolTool(
+				mockTask as Task,
+				block,
+				mockAskApproval,
+				mockHandleError,
+				mockPushToolResult,
+				mockRemoveClosingTag,
+			)
+
+			// Assert
+			expect(mockTask.consecutiveMistakeCount).toBe(1)
+			expect(mockTask.recordToolError).toHaveBeenCalledWith("use_mcp_tool")
+			expect(mockTask.say).toHaveBeenCalledWith("error", expect.stringContaining("not configured"))
+			expect(mockPushToolResult).toHaveBeenCalledWith(expect.stringContaining("s1"))
+			expect(callToolMock).not.toHaveBeenCalled()
+			expect(mockAskApproval).not.toHaveBeenCalled()
+		})
+
+		it("should reject unknown server names when no servers are available", async () => {
+			// Arrange
+			mockTask.consecutiveMistakeCount = 0
+
+			const callToolMock = vi.fn()
+			mockProviderRef.deref.mockReturnValue({
+				getMcpHub: () => ({
+					getAllServers: vi.fn().mockReturnValue([]),
+					callTool: callToolMock,
+				}),
+				postMessageToWebview: vi.fn(),
+			})
+
+			const block: ToolUse = {
+				type: "tool_use",
+				name: "use_mcp_tool",
+				params: {
+					server_name: "unknown",
+					tool_name: "any-tool",
+					arguments: "{}",
+				},
+				partial: false,
+			}
+
+			// Act
+			await useMcpToolTool(
+				mockTask as Task,
+				block,
+				mockAskApproval,
+				mockHandleError,
+				mockPushToolResult,
+				mockRemoveClosingTag,
+			)
+
+			// Assert
+			expect(mockTask.consecutiveMistakeCount).toBe(1)
+			expect(mockTask.recordToolError).toHaveBeenCalledWith("use_mcp_tool")
+			expect(mockTask.say).toHaveBeenCalledWith("error", expect.stringContaining("not configured"))
+			expect(mockPushToolResult).toHaveBeenCalledWith(expect.stringContaining("No servers available"))
+			expect(callToolMock).not.toHaveBeenCalled()
+			expect(mockAskApproval).not.toHaveBeenCalled()
+		})
 	})
 })

+ 112 - 0
src/core/tools/useMcpToolTool.ts

@@ -81,6 +81,112 @@ async function validateParams(
 	}
 }
 
+async function validateToolExists(
+	cline: Task,
+	serverName: string,
+	toolName: string,
+	pushToolResult: PushToolResult,
+): Promise<{ isValid: boolean; availableTools?: string[] }> {
+	try {
+		// Get the MCP hub to access server information
+		const provider = cline.providerRef.deref()
+		const mcpHub = provider?.getMcpHub()
+
+		if (!mcpHub) {
+			// If we can't get the MCP hub, we can't validate, so proceed with caution
+			return { isValid: true }
+		}
+
+		// Get all servers to find the specific one
+		const servers = mcpHub.getAllServers()
+		const server = servers.find((s) => s.name === serverName)
+
+		if (!server) {
+			// Fail fast when server is unknown
+			const availableServersArray = servers.map((s) => s.name)
+			const availableServers =
+				availableServersArray.length > 0 ? availableServersArray.join(", ") : "No servers available"
+
+			cline.consecutiveMistakeCount++
+			cline.recordToolError("use_mcp_tool")
+			await cline.say("error", t("mcp:errors.serverNotFound", { serverName, availableServers }))
+
+			pushToolResult(formatResponse.unknownMcpServerError(serverName, availableServersArray))
+			return { isValid: false, availableTools: [] }
+		}
+
+		// Check if the server has tools defined
+		if (!server.tools || server.tools.length === 0) {
+			// No tools available on this server
+			cline.consecutiveMistakeCount++
+			cline.recordToolError("use_mcp_tool")
+			await cline.say(
+				"error",
+				t("mcp:errors.toolNotFound", {
+					toolName,
+					serverName,
+					availableTools: "No tools available",
+				}),
+			)
+
+			pushToolResult(formatResponse.unknownMcpToolError(serverName, toolName, []))
+			return { isValid: false, availableTools: [] }
+		}
+
+		// Check if the requested tool exists
+		const tool = server.tools.find((tool) => tool.name === toolName)
+
+		if (!tool) {
+			// Tool not found - provide list of available tools
+			const availableToolNames = server.tools.map((tool) => tool.name)
+
+			cline.consecutiveMistakeCount++
+			cline.recordToolError("use_mcp_tool")
+			await cline.say(
+				"error",
+				t("mcp:errors.toolNotFound", {
+					toolName,
+					serverName,
+					availableTools: availableToolNames.join(", "),
+				}),
+			)
+
+			pushToolResult(formatResponse.unknownMcpToolError(serverName, toolName, availableToolNames))
+			return { isValid: false, availableTools: availableToolNames }
+		}
+
+		// Check if the tool is disabled (enabledForPrompt is false)
+		if (tool.enabledForPrompt === false) {
+			// Tool is disabled - only show enabled tools
+			const enabledTools = server.tools.filter((t) => t.enabledForPrompt !== false)
+			const enabledToolNames = enabledTools.map((t) => t.name)
+
+			cline.consecutiveMistakeCount++
+			cline.recordToolError("use_mcp_tool")
+			await cline.say(
+				"error",
+				t("mcp:errors.toolDisabled", {
+					toolName,
+					serverName,
+					availableTools:
+						enabledToolNames.length > 0 ? enabledToolNames.join(", ") : "No enabled tools available",
+				}),
+			)
+
+			pushToolResult(formatResponse.unknownMcpToolError(serverName, toolName, enabledToolNames))
+			return { isValid: false, availableTools: enabledToolNames }
+		}
+
+		// Tool exists and is enabled
+		return { isValid: true, availableTools: server.tools.map((tool) => tool.name) }
+	} catch (error) {
+		// If there's an error during validation, log it but don't block the tool execution
+		// The actual tool call might still fail with a proper error
+		console.error("Error validating MCP tool existence:", error)
+		return { isValid: true }
+	}
+}
+
 async function sendExecutionStatus(cline: Task, status: McpExecutionStatus): Promise<void> {
 	const clineProvider = await cline.providerRef.deref()
 	clineProvider?.postMessageToWebview({
@@ -193,6 +299,12 @@ export async function useMcpToolTool(
 
 		const { serverName, toolName, parsedArguments } = validation
 
+		// Validate that the tool exists on the server
+		const toolValidation = await validateToolExists(cline, serverName, toolName, pushToolResult)
+		if (!toolValidation.isValid) {
+			return
+		}
+
 		// Reset mistake count on successful validation
 		cline.consecutiveMistakeCount = 0
 

+ 4 - 1
src/i18n/locales/ca/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Roo ha intentat utilitzar {{toolName}} amb un argument JSON no vàlid. Tornant a intentar...",
 		"refresh_after_disable": "Ha fallat l'actualització de les connexions MCP després de desactivar",
 		"refresh_after_enable": "Ha fallat l'actualització de les connexions MCP després d'activar",
-		"disconnect_servers_partial": "Ha fallat la desconnexió de {{count}} servidor(s) MCP. Comprova la sortida per més detalls."
+		"disconnect_servers_partial": "Ha fallat la desconnexió de {{count}} servidor(s) MCP. Comprova la sortida per més detalls.",
+		"toolNotFound": "L'eina '{{toolName}}' no existeix al servidor '{{serverName}}'. Eines disponibles: {{availableTools}}",
+		"serverNotFound": "El servidor MCP '{{serverName}}' no està configurat. Servidors disponibles: {{availableServers}}",
+		"toolDisabled": "L'eina '{{toolName}}' del servidor '{{serverName}}' està desactivada. Eines activades disponibles: {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "Reiniciant el servidor MCP {{serverName}}...",

+ 4 - 1
src/i18n/locales/de/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Roo hat versucht, {{toolName}} mit einem ungültigen JSON-Argument zu verwenden. Wiederhole...",
 		"refresh_after_disable": "Fehler beim Aktualisieren der MCP-Verbindungen nach dem Deaktivieren",
 		"refresh_after_enable": "Fehler beim Aktualisieren der MCP-Verbindungen nach dem Aktivieren",
-		"disconnect_servers_partial": "Fehler beim Trennen von {{count}} MCP-Server(n). Überprüfe die Ausgabe für Details."
+		"disconnect_servers_partial": "Fehler beim Trennen von {{count}} MCP-Server(n). Überprüfe die Ausgabe für Details.",
+		"toolNotFound": "Tool '{{toolName}}' existiert nicht auf Server '{{serverName}}'. Verfügbare Tools: {{availableTools}}",
+		"serverNotFound": "MCP-Server '{{serverName}}' ist nicht konfiguriert. Verfügbare Server: {{availableServers}}",
+		"toolDisabled": "Tool '{{toolName}}' auf Server '{{serverName}}' ist deaktiviert. Verfügbare aktivierte Tools: {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "MCP-Server {{serverName}} wird neu gestartet...",

+ 4 - 1
src/i18n/locales/en/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Roo tried to use {{toolName}} with an invalid JSON argument. Retrying...",
 		"refresh_after_disable": "Failed to refresh MCP connections after disabling",
 		"refresh_after_enable": "Failed to refresh MCP connections after enabling",
-		"disconnect_servers_partial": "Failed to disconnect {{count}} MCP server(s). Check the output for details."
+		"disconnect_servers_partial": "Failed to disconnect {{count}} MCP server(s). Check the output for details.",
+		"toolNotFound": "Tool '{{toolName}}' does not exist on server '{{serverName}}'. Available tools: {{availableTools}}",
+		"serverNotFound": "MCP server '{{serverName}}' is not configured. Available servers: {{availableServers}}",
+		"toolDisabled": "Tool '{{toolName}}' on server '{{serverName}}' is disabled. Available enabled tools: {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "Restarting {{serverName}} MCP server...",

+ 4 - 1
src/i18n/locales/es/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Roo intentó usar {{toolName}} con un argumento JSON no válido. Reintentando...",
 		"refresh_after_disable": "Error al actualizar las conexiones MCP después de desactivar",
 		"refresh_after_enable": "Error al actualizar las conexiones MCP después de activar",
-		"disconnect_servers_partial": "Error al desconectar {{count}} servidor(es) MCP. Revisa la salida para más detalles."
+		"disconnect_servers_partial": "Error al desconectar {{count}} servidor(es) MCP. Revisa la salida para más detalles.",
+		"toolNotFound": "La herramienta '{{toolName}}' no existe en el servidor '{{serverName}}'. Herramientas disponibles: {{availableTools}}",
+		"serverNotFound": "El servidor MCP '{{serverName}}' no está configurado. Servidores disponibles: {{availableServers}}",
+		"toolDisabled": "La herramienta '{{toolName}}' del servidor '{{serverName}}' está desactivada. Herramientas activadas disponibles: {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "Reiniciando el servidor MCP {{serverName}}...",

+ 4 - 1
src/i18n/locales/fr/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Roo a essayé d'utiliser {{toolName}} avec un argument JSON invalide. Nouvelle tentative...",
 		"refresh_after_disable": "Échec du rafraîchissement des connexions MCP après désactivation",
 		"refresh_after_enable": "Échec du rafraîchissement des connexions MCP après activation",
-		"disconnect_servers_partial": "Échec de la déconnexion de {{count}} serveur(s) MCP. Vérifiez la sortie pour plus de détails."
+		"disconnect_servers_partial": "Échec de la déconnexion de {{count}} serveur(s) MCP. Vérifiez la sortie pour plus de détails.",
+		"toolNotFound": "L'outil '{{toolName}}' n'existe pas sur le serveur '{{serverName}}'. Outils disponibles : {{availableTools}}",
+		"serverNotFound": "Le serveur MCP '{{serverName}}' n'est pas configuré. Serveurs disponibles : {{availableServers}}",
+		"toolDisabled": "L'outil '{{toolName}}' sur le serveur '{{serverName}}' est désactivé. Outils activés disponibles : {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "Redémarrage du serveur MCP {{serverName}}...",

+ 4 - 1
src/i18n/locales/hi/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Roo ने {{toolName}} को अमान्य JSON आर्गुमेंट के साथ उपयोग करने की कोशिश की। फिर से कोशिश कर रहा है...",
 		"refresh_after_disable": "अक्षम करने के बाद MCP कनेक्शन रीफ्रेश करने में विफल",
 		"refresh_after_enable": "सक्षम करने के बाद MCP कनेक्शन रीफ्रेश करने में विफल",
-		"disconnect_servers_partial": "{{count}} MCP सर्वर डिस्कनेक्ट करने में विफल। विवरण के लिए आउटपुट देखें।"
+		"disconnect_servers_partial": "{{count}} MCP सर्वर डिस्कनेक्ट करने में विफल। विवरण के लिए आउटपुट देखें।",
+		"toolNotFound": "टूल '{{toolName}}' सर्वर '{{serverName}}' पर मौजूद नहीं है। उपलब्ध टूल: {{availableTools}}",
+		"serverNotFound": "MCP सर्वर '{{serverName}}' कॉन्फ़िगर नहीं है। उपलब्ध सर्वर: {{availableServers}}",
+		"toolDisabled": "सर्वर '{{serverName}}' पर टूल '{{toolName}}' अक्षम है। उपलब्ध सक्षम टूल: {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "{{serverName}} MCP सर्वर पुनः प्रारंभ हो रहा है...",

+ 4 - 1
src/i18n/locales/id/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Roo mencoba menggunakan {{toolName}} dengan argumen JSON yang tidak valid. Mencoba lagi...",
 		"refresh_after_disable": "Gagal me-refresh koneksi MCP setelah menonaktifkan",
 		"refresh_after_enable": "Gagal me-refresh koneksi MCP setelah mengaktifkan",
-		"disconnect_servers_partial": "Gagal memutus koneksi {{count}} server MCP. Periksa output untuk detailnya."
+		"disconnect_servers_partial": "Gagal memutus koneksi {{count}} server MCP. Periksa output untuk detailnya.",
+		"toolNotFound": "Tool '{{toolName}}' tidak ada di server '{{serverName}}'. Tool yang tersedia: {{availableTools}}",
+		"serverNotFound": "Server MCP '{{serverName}}' tidak dikonfigurasi. Server yang tersedia: {{availableServers}}",
+		"toolDisabled": "Tool '{{toolName}}' di server '{{serverName}}' dinonaktifkan. Tool aktif yang tersedia: {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "Merestart server MCP {{serverName}}...",

+ 4 - 1
src/i18n/locales/it/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Roo ha tentato di usare {{toolName}} con un argomento JSON non valido. Riprovo...",
 		"refresh_after_disable": "Impossibile aggiornare le connessioni MCP dopo la disattivazione",
 		"refresh_after_enable": "Impossibile aggiornare le connessioni MCP dopo l'attivazione",
-		"disconnect_servers_partial": "Impossibile disconnettere {{count}} server MCP. Controlla l'output per i dettagli."
+		"disconnect_servers_partial": "Impossibile disconnettere {{count}} server MCP. Controlla l'output per i dettagli.",
+		"toolNotFound": "Lo strumento '{{toolName}}' non esiste sul server '{{serverName}}'. Strumenti disponibili: {{availableTools}}",
+		"serverNotFound": "Il server MCP '{{serverName}}' non è configurato. Server disponibili: {{availableServers}}",
+		"toolDisabled": "Lo strumento '{{toolName}}' sul server '{{serverName}}' è disabilitato. Strumenti abilitati disponibili: {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "Riavvio del server MCP {{serverName}}...",

+ 4 - 1
src/i18n/locales/ja/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Rooが無効なJSON引数で{{toolName}}を使用しようとしました。再試行中...",
 		"refresh_after_disable": "無効化後にMCP接続の更新に失敗しました",
 		"refresh_after_enable": "有効化後にMCP接続の更新に失敗しました",
-		"disconnect_servers_partial": "{{count}}個のMCPサーバーの切断に失敗しました。詳細は出力を確認してください。"
+		"disconnect_servers_partial": "{{count}}個のMCPサーバーの切断に失敗しました。詳細は出力を確認してください。",
+		"toolNotFound": "ツール '{{toolName}}' はサーバー '{{serverName}}' に存在しません。利用可能なツール: {{availableTools}}",
+		"serverNotFound": "MCPサーバー '{{serverName}}' は設定されていません。利用可能なサーバー: {{availableServers}}",
+		"toolDisabled": "サーバー '{{serverName}}' のツール '{{toolName}}' は無効です。利用可能な有効なツール: {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "MCPサーバー{{serverName}}を再起動中...",

+ 4 - 1
src/i18n/locales/ko/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Roo가 유효하지 않은 JSON 인자로 {{toolName}}을(를) 사용하려고 했습니다. 다시 시도 중...",
 		"refresh_after_disable": "비활성화 후 MCP 연결 새로 고침 실패",
 		"refresh_after_enable": "활성화 후 MCP 연결 새로 고침 실패",
-		"disconnect_servers_partial": "{{count}}개의 MCP 서버 연결 해제 실패. 자세한 내용은 출력을 확인하세요."
+		"disconnect_servers_partial": "{{count}}개의 MCP 서버 연결 해제 실패. 자세한 내용은 출력을 확인하세요.",
+		"toolNotFound": "도구 '{{toolName}}'이(가) 서버 '{{serverName}}'에 존재하지 않습니다. 사용 가능한 도구: {{availableTools}}",
+		"serverNotFound": "MCP 서버 '{{serverName}}'이(가) 구성되지 않았습니다. 사용 가능한 서버: {{availableServers}}",
+		"toolDisabled": "서버 '{{serverName}}'의 도구 '{{toolName}}'이(가) 비활성화되었습니다. 사용 가능한 활성화된 도구: {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "{{serverName}} MCP 서버를 재시작하는 중...",

+ 4 - 1
src/i18n/locales/nl/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Roo probeerde {{toolName}} te gebruiken met een ongeldig JSON-argument. Opnieuw proberen...",
 		"refresh_after_disable": "Vernieuwen van MCP-verbindingen na uitschakelen mislukt",
 		"refresh_after_enable": "Vernieuwen van MCP-verbindingen na inschakelen mislukt",
-		"disconnect_servers_partial": "Loskoppelen van {{count}} MCP-server(s) mislukt. Controleer de uitvoer voor details."
+		"disconnect_servers_partial": "Loskoppelen van {{count}} MCP-server(s) mislukt. Controleer de uitvoer voor details.",
+		"toolNotFound": "Tool '{{toolName}}' bestaat niet op server '{{serverName}}'. Beschikbare tools: {{availableTools}}",
+		"serverNotFound": "MCP-server '{{serverName}}' is niet geconfigureerd. Beschikbare servers: {{availableServers}}",
+		"toolDisabled": "Tool '{{toolName}}' op server '{{serverName}}' is uitgeschakeld. Beschikbare ingeschakelde tools: {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "{{serverName}} MCP-server wordt opnieuw gestart...",

+ 4 - 1
src/i18n/locales/pl/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Roo próbował użyć {{toolName}} z nieprawidłowym argumentem JSON. Ponawianie...",
 		"refresh_after_disable": "Nie udało się odświeżyć połączeń MCP po wyłączeniu",
 		"refresh_after_enable": "Nie udało się odświeżyć połączeń MCP po włączeniu",
-		"disconnect_servers_partial": "Nie udało się odłączyć {{count}} serwera(ów) MCP. Sprawdź dane wyjściowe, aby uzyskać szczegóły."
+		"disconnect_servers_partial": "Nie udało się odłączyć {{count}} serwera(ów) MCP. Sprawdź dane wyjściowe, aby uzyskać szczegóły.",
+		"toolNotFound": "Narzędzie '{{toolName}}' nie istnieje na serwerze '{{serverName}}'. Dostępne narzędzia: {{availableTools}}",
+		"serverNotFound": "Serwer MCP '{{serverName}}' nie jest skonfigurowany. Dostępne serwery: {{availableServers}}",
+		"toolDisabled": "Narzędzie '{{toolName}}' na serwerze '{{serverName}}' jest wyłączone. Dostępne włączone narzędzia: {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "Ponowne uruchamianie serwera MCP {{serverName}}...",

+ 4 - 1
src/i18n/locales/pt-BR/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Roo tentou usar {{toolName}} com um argumento JSON inválido. Tentando novamente...",
 		"refresh_after_disable": "Falha ao atualizar as conexões MCP após desativar",
 		"refresh_after_enable": "Falha ao atualizar as conexões MCP após ativar",
-		"disconnect_servers_partial": "Falha ao desconectar {{count}} servidor(es) MCP. Verifique a saída para detalhes."
+		"disconnect_servers_partial": "Falha ao desconectar {{count}} servidor(es) MCP. Verifique a saída para detalhes.",
+		"toolNotFound": "A ferramenta '{{toolName}}' não existe no servidor '{{serverName}}'. Ferramentas disponíveis: {{availableTools}}",
+		"serverNotFound": "O servidor MCP '{{serverName}}' não está configurado. Servidores disponíveis: {{availableServers}}",
+		"toolDisabled": "A ferramenta '{{toolName}}' no servidor '{{serverName}}' está desabilitada. Ferramentas habilitadas disponíveis: {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "Reiniciando o servidor MCP {{serverName}}...",

+ 4 - 1
src/i18n/locales/ru/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Roo попытался использовать {{toolName}} с недопустимым JSON-аргументом. Повторная попытка...",
 		"refresh_after_disable": "Не удалось обновить соединения MCP после отключения",
 		"refresh_after_enable": "Не удалось обновить соединения MCP после включения",
-		"disconnect_servers_partial": "Не удалось отключить {{count}} MCP сервер(ов). Проверьте вывод для получения подробностей."
+		"disconnect_servers_partial": "Не удалось отключить {{count}} MCP сервер(ов). Проверьте вывод для получения подробностей.",
+		"toolNotFound": "Инструмент '{{toolName}}' не существует на сервере '{{serverName}}'. Доступные инструменты: {{availableTools}}",
+		"serverNotFound": "MCP сервер '{{serverName}}' не настроен. Доступные серверы: {{availableServers}}",
+		"toolDisabled": "Инструмент '{{toolName}}' на сервере '{{serverName}}' отключен. Доступные включенные инструменты: {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "Перезапуск сервера MCP {{serverName}}...",

+ 4 - 1
src/i18n/locales/tr/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Roo, {{toolName}} aracını geçersiz bir JSON argümanıyla kullanmaya çalıştı. Tekrar deneniyor...",
 		"refresh_after_disable": "Devre dışı bıraktıktan sonra MCP bağlantıları yenilenemedi",
 		"refresh_after_enable": "Etkinleştirdikten sonra MCP bağlantıları yenilenemedi",
-		"disconnect_servers_partial": "{{count}} MCP sunucusu bağlantısı kesilemedi. Ayrıntılar için çıktıyı kontrol edin."
+		"disconnect_servers_partial": "{{count}} MCP sunucusu bağlantısı kesilemedi. Ayrıntılar için çıktıyı kontrol edin.",
+		"toolNotFound": "Araç '{{toolName}}' sunucu '{{serverName}}' üzerinde mevcut değil. Mevcut araçlar: {{availableTools}}",
+		"serverNotFound": "MCP sunucusu '{{serverName}}' yapılandırılmamış. Mevcut sunucular: {{availableServers}}",
+		"toolDisabled": "Sunucu '{{serverName}}' üzerindeki araç '{{toolName}}' devre dışı. Mevcut etkin araçlar: {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "{{serverName}} MCP sunucusu yeniden başlatılıyor...",

+ 4 - 1
src/i18n/locales/vi/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Roo đã cố gắng sử dụng {{toolName}} với tham số JSON không hợp lệ. Đang thử lại...",
 		"refresh_after_disable": "Không thể làm mới kết nối MCP sau khi vô hiệu hóa",
 		"refresh_after_enable": "Không thể làm mới kết nối MCP sau khi kích hoạt",
-		"disconnect_servers_partial": "Không thể ngắt kết nối {{count}} máy chủ MCP. Kiểm tra đầu ra để biết chi tiết."
+		"disconnect_servers_partial": "Không thể ngắt kết nối {{count}} máy chủ MCP. Kiểm tra đầu ra để biết chi tiết.",
+		"toolNotFound": "Công cụ '{{toolName}}' không tồn tại trên máy chủ '{{serverName}}'. Công cụ có sẵn: {{availableTools}}",
+		"serverNotFound": "Máy chủ MCP '{{serverName}}' chưa được cấu hình. Máy chủ có sẵn: {{availableServers}}",
+		"toolDisabled": "Công cụ '{{toolName}}' trên máy chủ '{{serverName}}' đã bị vô hiệu hóa. Công cụ đã kích hoạt có sẵn: {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "Đang khởi động lại máy chủ MCP {{serverName}}...",

+ 4 - 1
src/i18n/locales/zh-CN/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Roo 尝试使用无效的 JSON 参数调用 {{toolName}}。正在重试...",
 		"refresh_after_disable": "禁用后刷新 MCP 连接失败",
 		"refresh_after_enable": "启用后刷新 MCP 连接失败",
-		"disconnect_servers_partial": "断开 {{count}} 个 MCP 服务器失败。请查看输出了解详情。"
+		"disconnect_servers_partial": "断开 {{count}} 个 MCP 服务器失败。请查看输出了解详情。",
+		"toolNotFound": "工具 '{{toolName}}' 在服务器 '{{serverName}}' 上不存在。可用工具: {{availableTools}}",
+		"serverNotFound": "MCP 服务器 '{{serverName}}' 未配置。可用服务器: {{availableServers}}",
+		"toolDisabled": "服务器 '{{serverName}}' 上的工具 '{{toolName}}' 已禁用。可用的已启用工具: {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "正在重启{{serverName}}MCP服务器...",

+ 4 - 1
src/i18n/locales/zh-TW/mcp.json

@@ -8,7 +8,10 @@
 		"invalidJsonArgument": "Roo 嘗試使用無效的 JSON 參數呼叫 {{toolName}}。正在重試...",
 		"refresh_after_disable": "停用後重新整理 MCP 連線失敗",
 		"refresh_after_enable": "啟用後重新整理 MCP 連線失敗",
-		"disconnect_servers_partial": "斷開 {{count}} 個 MCP 伺服器失敗。請查看輸出了解詳情。"
+		"disconnect_servers_partial": "斷開 {{count}} 個 MCP 伺服器失敗。請查看輸出了解詳情。",
+		"toolNotFound": "工具 '{{toolName}}' 在伺服器 '{{serverName}}' 上不存在。可用工具: {{availableTools}}",
+		"serverNotFound": "MCP 伺服器 '{{serverName}}' 未設定。可用伺服器: {{availableServers}}",
+		"toolDisabled": "伺服器 '{{serverName}}' 上的工具 '{{toolName}}' 已停用。可用的已啟用工具: {{availableTools}}"
 	},
 	"info": {
 		"server_restarting": "正在重啟{{serverName}}MCP 伺服器...",