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..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 { @@ -354,6 +360,40 @@ suite("Roo Code use_mcp_tool Tool", function () { } api.on("taskCompleted", taskCompletedHandler) + // 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) + } + + // 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 @@ -482,6 +522,40 @@ suite("Roo Code use_mcp_tool Tool", function () { } api.on("taskCompleted", taskCompletedHandler) + // 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) + } + + // 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 @@ -621,6 +695,40 @@ suite("Roo Code use_mcp_tool Tool", function () { } api.on("taskCompleted", taskCompletedHandler) + // 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) + } + + // 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 @@ -842,6 +950,42 @@ suite("Roo Code use_mcp_tool Tool", function () { } api.on("taskCompleted", taskCompletedHandler) + // 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...") + 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) + } + + // 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 @@ -857,7 +1001,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") 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") + } } }