From 5cb60d9430e946a5a06b71f0780adec840842aaa Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Mon, 23 Jun 2025 10:29:19 -0500 Subject: [PATCH 1/2] fix: ensure MCP test independence by adding config setup to all tests --- .../src/suite/tools/use-mcp-tool.test.ts | 134 +++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/apps/vscode-e2e/src/suite/tools/use-mcp-tool.test.ts b/apps/vscode-e2e/src/suite/tools/use-mcp-tool.test.ts index df57ae88c6..e094fd65a6 100644 --- a/apps/vscode-e2e/src/suite/tools/use-mcp-tool.test.ts +++ b/apps/vscode-e2e/src/suite/tools/use-mcp-tool.test.ts @@ -353,7 +353,40 @@ suite("Roo Code use_mcp_tool Tool", function () { } } api.on("taskCompleted", taskCompletedHandler) + await sleep(2000) // Wait for Roo Code to fully initialize + + // Trigger MCP server detection by opening and modifying the file + console.log("Triggering MCP server detection by modifying the config file...") + try { + const mcpConfigUri = vscode.Uri.file(testFiles.mcpConfig) + const document = await vscode.workspace.openTextDocument(mcpConfigUri) + const editor = await vscode.window.showTextDocument(document) + + // Make a small modification to trigger the save event, without this Roo Code won't load the MCP server + const edit = new vscode.WorkspaceEdit() + const currentContent = document.getText() + const modifiedContent = currentContent.replace( + '"alwaysAllow": []', + '"alwaysAllow": ["read_file", "read_multiple_files", "write_file", "edit_file", "create_directory", "list_directory", "directory_tree", "move_file", "search_files", "get_file_info", "list_allowed_directories"]', + ) + + const fullRange = new vscode.Range(document.positionAt(0), document.positionAt(document.getText().length)) + edit.replace(mcpConfigUri, fullRange, modifiedContent) + await vscode.workspace.applyEdit(edit) + + // Save the document to trigger MCP server detection + await editor.document.save() + + // Close the editor + await vscode.commands.executeCommand("workbench.action.closeActiveEditor") + + console.log("MCP config file modified and saved successfully") + } catch (error) { + console.error("Failed to modify/save MCP config file:", error) + } + + await sleep(5000) // Wait for MCP servers to initialize let taskId: string try { // Start task requesting to use MCP filesystem write_file tool @@ -481,7 +514,40 @@ suite("Roo Code use_mcp_tool Tool", function () { } } api.on("taskCompleted", taskCompletedHandler) + await sleep(2000) // Wait for Roo Code to fully initialize + // Trigger MCP server detection by opening and modifying the file + console.log("Triggering MCP server detection by modifying the config file...") + try { + const mcpConfigUri = vscode.Uri.file(testFiles.mcpConfig) + const document = await vscode.workspace.openTextDocument(mcpConfigUri) + const editor = await vscode.window.showTextDocument(document) + + // Make a small modification to trigger the save event, without this Roo Code won't load the MCP server + const edit = new vscode.WorkspaceEdit() + const currentContent = document.getText() + const modifiedContent = currentContent.replace( + '"alwaysAllow": []', + '"alwaysAllow": ["read_file", "read_multiple_files", "write_file", "edit_file", "create_directory", "list_directory", "directory_tree", "move_file", "search_files", "get_file_info", "list_allowed_directories"]', + ) + + const fullRange = new vscode.Range(document.positionAt(0), document.positionAt(document.getText().length)) + + edit.replace(mcpConfigUri, fullRange, modifiedContent) + await vscode.workspace.applyEdit(edit) + + // Save the document to trigger MCP server detection + await editor.document.save() + + // Close the editor + await vscode.commands.executeCommand("workbench.action.closeActiveEditor") + + console.log("MCP config file modified and saved successfully") + } catch (error) { + console.error("Failed to modify/save MCP config file:", error) + } + + await sleep(5000) // Wait for MCP servers to initialize let taskId: string try { // Start task requesting MCP filesystem list_directory tool @@ -620,7 +686,40 @@ suite("Roo Code use_mcp_tool Tool", function () { } } api.on("taskCompleted", taskCompletedHandler) + await sleep(2000) // Wait for Roo Code to fully initialize + // Trigger MCP server detection by opening and modifying the file + console.log("Triggering MCP server detection by modifying the config file...") + try { + const mcpConfigUri = vscode.Uri.file(testFiles.mcpConfig) + const document = await vscode.workspace.openTextDocument(mcpConfigUri) + const editor = await vscode.window.showTextDocument(document) + + // Make a small modification to trigger the save event, without this Roo Code won't load the MCP server + const edit = new vscode.WorkspaceEdit() + const currentContent = document.getText() + const modifiedContent = currentContent.replace( + '"alwaysAllow": []', + '"alwaysAllow": ["read_file", "read_multiple_files", "write_file", "edit_file", "create_directory", "list_directory", "directory_tree", "move_file", "search_files", "get_file_info", "list_allowed_directories"]', + ) + + const fullRange = new vscode.Range(document.positionAt(0), document.positionAt(document.getText().length)) + + edit.replace(mcpConfigUri, fullRange, modifiedContent) + await vscode.workspace.applyEdit(edit) + + // Save the document to trigger MCP server detection + await editor.document.save() + + // Close the editor + await vscode.commands.executeCommand("workbench.action.closeActiveEditor") + + console.log("MCP config file modified and saved successfully") + } catch (error) { + console.error("Failed to modify/save MCP config file:", error) + } + + await sleep(5000) // Wait for MCP servers to initialize let taskId: string try { // Start task requesting MCP filesystem directory_tree tool @@ -841,7 +940,40 @@ suite("Roo Code use_mcp_tool Tool", function () { } } api.on("taskCompleted", taskCompletedHandler) + await sleep(2000) // Wait for Roo Code to fully initialize + // Trigger MCP server detection by opening and modifying the file + console.log("Triggering MCP server detection by modifying the config file...") + try { + const mcpConfigUri = vscode.Uri.file(testFiles.mcpConfig) + const document = await vscode.workspace.openTextDocument(mcpConfigUri) + const editor = await vscode.window.showTextDocument(document) + + // Make a small modification to trigger the save event, without this Roo Code won't load the MCP server + const edit = new vscode.WorkspaceEdit() + const currentContent = document.getText() + const modifiedContent = currentContent.replace( + '"alwaysAllow": []', + '"alwaysAllow": ["read_file", "read_multiple_files", "write_file", "edit_file", "create_directory", "list_directory", "directory_tree", "move_file", "search_files", "get_file_info", "list_allowed_directories"]', + ) + + const fullRange = new vscode.Range(document.positionAt(0), document.positionAt(document.getText().length)) + + edit.replace(mcpConfigUri, fullRange, modifiedContent) + await vscode.workspace.applyEdit(edit) + + // Save the document to trigger MCP server detection + await editor.document.save() + + // Close the editor + await vscode.commands.executeCommand("workbench.action.closeActiveEditor") + + console.log("MCP config file modified and saved successfully") + } catch (error) { + console.error("Failed to modify/save MCP config file:", error) + } + + await sleep(5000) // Wait for MCP servers to initialize let taskId: string try { // Start task requesting MCP filesystem get_file_info tool @@ -857,7 +989,7 @@ suite("Roo Code use_mcp_tool Tool", function () { }) // Wait for attempt_completion to be called (indicating task finished) - await waitFor(() => attemptCompletionCalled, { timeout: 45_000 }) + await waitFor(() => attemptCompletionCalled, { timeout: 50_000 }) // Verify the MCP tool was requested with valid format assert.ok(mcpToolRequested, "The use_mcp_tool should have been requested") From e52478fac304561f55f10881f48245eb41cfaa09 Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Tue, 24 Jun 2025 19:28:01 -0500 Subject: [PATCH 2/2] feat: add mcpServersInitialized event to API and ClineProvider, emit on MCP server initialization --- .../src/suite/tools/use-mcp-tool.test.ts | 42 ++++++++++++------- packages/types/src/api.ts | 1 + packages/types/src/ipc.ts | 7 ++++ src/core/webview/ClineProvider.ts | 1 + src/extension/api.ts | 5 +++ src/services/mcp/McpHub.ts | 5 +++ 6 files changed, 46 insertions(+), 15 deletions(-) diff --git a/apps/vscode-e2e/src/suite/tools/use-mcp-tool.test.ts b/apps/vscode-e2e/src/suite/tools/use-mcp-tool.test.ts index e094fd65a6..17ea8ed0d1 100644 --- a/apps/vscode-e2e/src/suite/tools/use-mcp-tool.test.ts +++ b/apps/vscode-e2e/src/suite/tools/use-mcp-tool.test.ts @@ -26,11 +26,16 @@ suite("Roo Code use_mcp_tool Tool", function () { // Create test files in VSCode workspace directory const workspaceDir = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || tempDir + // Resolve the real path to avoid symlink issues on macOS + const realWorkspaceDir = await fs.realpath(workspaceDir) + console.log("Original workspace dir:", workspaceDir) + console.log("Real workspace dir:", realWorkspaceDir) + // Create test files for MCP filesystem operations testFiles = { - simple: path.join(workspaceDir, `mcp-test-${Date.now()}.txt`), - testData: path.join(workspaceDir, `mcp-data-${Date.now()}.json`), - mcpConfig: path.join(workspaceDir, ".roo", "mcp.json"), + simple: path.join(realWorkspaceDir, `mcp-test-${Date.now()}.txt`), + testData: path.join(realWorkspaceDir, `mcp-data-${Date.now()}.json`), + mcpConfig: path.join(realWorkspaceDir, ".roo", "mcp.json"), } // Create initial test files @@ -38,21 +43,21 @@ suite("Roo Code use_mcp_tool Tool", function () { await fs.writeFile(testFiles.testData, JSON.stringify({ test: "data", value: 42 }, null, 2)) // Create .roo directory and MCP configuration file - const rooDir = path.join(workspaceDir, ".roo") + const rooDir = path.join(realWorkspaceDir, ".roo") await fs.mkdir(rooDir, { recursive: true }) const mcpConfig = { mcpServers: { filesystem: { command: "npx", - args: ["-y", "@modelcontextprotocol/server-filesystem", workspaceDir], + args: ["-y", "@modelcontextprotocol/server-filesystem", realWorkspaceDir], alwaysAllow: [], }, }, } await fs.writeFile(testFiles.mcpConfig, JSON.stringify(mcpConfig, null, 2)) - console.log("MCP test files created in:", workspaceDir) + console.log("MCP test files created in:", realWorkspaceDir) console.log("Test files:", testFiles) }) @@ -76,7 +81,8 @@ suite("Roo Code use_mcp_tool Tool", function () { // Clean up .roo directory const workspaceDir = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || tempDir - const rooDir = path.join(workspaceDir, ".roo") + const realWorkspaceDir = await fs.realpath(workspaceDir) + const rooDir = path.join(realWorkspaceDir, ".roo") try { await fs.rm(rooDir, { recursive: true, force: true }) } catch { @@ -353,7 +359,6 @@ suite("Roo Code use_mcp_tool Tool", function () { } } api.on("taskCompleted", taskCompletedHandler) - await sleep(2000) // Wait for Roo Code to fully initialize // Trigger MCP server detection by opening and modifying the file console.log("Triggering MCP server detection by modifying the config file...") @@ -386,7 +391,9 @@ suite("Roo Code use_mcp_tool Tool", function () { console.error("Failed to modify/save MCP config file:", error) } - await sleep(5000) // Wait for MCP servers to initialize + // Give MCP servers a moment to be ready (they should already be initialized) + await sleep(1000) + let taskId: string try { // Start task requesting to use MCP filesystem write_file tool @@ -514,7 +521,6 @@ suite("Roo Code use_mcp_tool Tool", function () { } } api.on("taskCompleted", taskCompletedHandler) - await sleep(2000) // Wait for Roo Code to fully initialize // Trigger MCP server detection by opening and modifying the file console.log("Triggering MCP server detection by modifying the config file...") @@ -547,7 +553,9 @@ suite("Roo Code use_mcp_tool Tool", function () { console.error("Failed to modify/save MCP config file:", error) } - await sleep(5000) // Wait for MCP servers to initialize + // Give MCP servers a moment to be ready (they should already be initialized) + await sleep(1000) + let taskId: string try { // Start task requesting MCP filesystem list_directory tool @@ -686,7 +694,6 @@ suite("Roo Code use_mcp_tool Tool", function () { } } api.on("taskCompleted", taskCompletedHandler) - await sleep(2000) // Wait for Roo Code to fully initialize // Trigger MCP server detection by opening and modifying the file console.log("Triggering MCP server detection by modifying the config file...") @@ -719,7 +726,9 @@ suite("Roo Code use_mcp_tool Tool", function () { console.error("Failed to modify/save MCP config file:", error) } - await sleep(5000) // Wait for MCP servers to initialize + // Give MCP servers a moment to be ready (they should already be initialized) + await sleep(1000) + let taskId: string try { // Start task requesting MCP filesystem directory_tree tool @@ -940,7 +949,8 @@ suite("Roo Code use_mcp_tool Tool", function () { } } api.on("taskCompleted", taskCompletedHandler) - await sleep(2000) // Wait for Roo Code to fully initialize + + // Wait for MCP servers to be initialized instead of using sleep // Trigger MCP server detection by opening and modifying the file console.log("Triggering MCP server detection by modifying the config file...") @@ -973,7 +983,9 @@ suite("Roo Code use_mcp_tool Tool", function () { console.error("Failed to modify/save MCP config file:", error) } - await sleep(5000) // Wait for MCP servers to initialize + // Give MCP servers a moment to be ready (they should already be initialized) + await sleep(1000) + let taskId: string try { // Start task requesting MCP filesystem get_file_info tool diff --git a/packages/types/src/api.ts b/packages/types/src/api.ts index 6fb181b573..24c236c19c 100644 --- a/packages/types/src/api.ts +++ b/packages/types/src/api.ts @@ -21,6 +21,7 @@ export interface RooCodeAPIEvents { taskCompleted: [taskId: string, tokenUsage: TokenUsage, toolUsage: ToolUsage, isSubtask: IsSubtask] taskTokenUsageUpdated: [taskId: string, tokenUsage: TokenUsage] taskToolFailed: [taskId: string, toolName: ToolName, error: string] + mcpServersInitialized: [] } export interface RooCodeAPI extends EventEmitter { diff --git a/packages/types/src/ipc.ts b/packages/types/src/ipc.ts index 28accde9de..fcd8e982e8 100644 --- a/packages/types/src/ipc.ts +++ b/packages/types/src/ipc.ts @@ -29,6 +29,7 @@ export enum RooCodeEventName { TaskCompleted = "taskCompleted", TaskTokenUsageUpdated = "taskTokenUsageUpdated", TaskToolFailed = "taskToolFailed", + McpServersInitialized = "mcpServersInitialized", EvalPass = "evalPass", EvalFail = "evalFail", } @@ -52,6 +53,7 @@ export const rooCodeEventsSchema = z.object({ [RooCodeEventName.TaskCompleted]: z.tuple([z.string(), tokenUsageSchema, toolUsageSchema, isSubtaskSchema]), [RooCodeEventName.TaskTokenUsageUpdated]: z.tuple([z.string(), tokenUsageSchema]), [RooCodeEventName.TaskToolFailed]: z.tuple([z.string(), toolNamesSchema, z.string()]), + [RooCodeEventName.McpServersInitialized]: z.tuple([]), }) export type RooCodeEvents = z.infer @@ -165,6 +167,11 @@ export const taskEventSchema = z.discriminatedUnion("eventName", [ payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskToolFailed], taskId: z.number().optional(), }), + z.object({ + eventName: z.literal(RooCodeEventName.McpServersInitialized), + payload: rooCodeEventsSchema.shape[RooCodeEventName.McpServersInitialized], + taskId: z.number().optional(), + }), z.object({ eventName: z.literal(RooCodeEventName.EvalPass), payload: z.undefined(), diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index dff2263a06..8c9d8a3465 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -76,6 +76,7 @@ import { ProfileValidator } from "../../shared/ProfileValidator" export type ClineProviderEvents = { clineCreated: [cline: Task] + mcpServersInitialized: [] } class OrganizationAllowListViolationError extends Error { diff --git a/src/extension/api.ts b/src/extension/api.ts index 3bb538dcb3..1895cd8d50 100644 --- a/src/extension/api.ts +++ b/src/extension/api.ts @@ -248,6 +248,11 @@ export class API extends EventEmitter implements RooCodeAPI { this.emit(RooCodeEventName.TaskCreated, cline.taskId) }) + + // Listen for MCP servers initialized event + provider.on("mcpServersInitialized", () => { + this.emit(RooCodeEventName.McpServersInitialized) + }) } // Logging diff --git a/src/services/mcp/McpHub.ts b/src/services/mcp/McpHub.ts index 10a74712ef..5dba64f53e 100644 --- a/src/services/mcp/McpHub.ts +++ b/src/services/mcp/McpHub.ts @@ -995,6 +995,11 @@ export class McpHub { await this.notifyWebviewOfServerChanges() if (manageConnectingState) { this.isConnecting = false + // Emit MCP servers initialized event + const provider = this.providerRef.deref() + if (provider) { + provider.emit("mcpServersInitialized") + } } }