Просмотр исходного кода

Revert "Merge pull request #1618 from aheizi/support_project_mcp" (#1784)

This reverts commit df80e9620d465c00528dfc08746140b2ab9021df, reversing
changes made to dc302f72c2670a55aed1e159cb5a591157b7bc4c.
Matt Rubens 9 месяцев назад
Родитель
Сommit
2267cad93d

+ 0 - 22
src/core/webview/ClineProvider.ts

@@ -1183,28 +1183,6 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
 						}
 						break
 					}
-					case "openProjectMcpSettings": {
-						if (!vscode.workspace.workspaceFolders?.length) {
-							vscode.window.showErrorMessage(t("common:errors.no_workspace"))
-							return
-						}
-
-						const workspaceFolder = vscode.workspace.workspaceFolders[0]
-						const rooDir = path.join(workspaceFolder.uri.fsPath, ".roo")
-						const mcpPath = path.join(rooDir, "mcp.json")
-
-						try {
-							await fs.mkdir(rooDir, { recursive: true })
-							const exists = await fileExistsAtPath(mcpPath)
-							if (!exists) {
-								await fs.writeFile(mcpPath, JSON.stringify({ mcpServers: {} }, null, 2))
-							}
-							await openFile(mcpPath)
-						} catch (error) {
-							vscode.window.showErrorMessage(t("common:errors.create_mcp_json", { error }))
-						}
-						break
-					}
 					case "openCustomModesSettings": {
 						const customModesFilePath = await this.customModesManager.getCustomModesFilePath()
 						if (customModesFilePath) {

+ 0 - 124
src/core/webview/__tests__/ClineProvider.test.ts

@@ -2055,130 +2055,6 @@ describe("ClineProvider", () => {
 	})
 })
 
-describe("Project MCP Settings", () => {
-	let provider: ClineProvider
-	let mockContext: vscode.ExtensionContext
-	let mockOutputChannel: vscode.OutputChannel
-	let mockWebviewView: vscode.WebviewView
-	let mockPostMessage: jest.Mock
-
-	beforeEach(() => {
-		jest.clearAllMocks()
-
-		mockContext = {
-			extensionPath: "/test/path",
-			extensionUri: {} as vscode.Uri,
-			globalState: {
-				get: jest.fn(),
-				update: jest.fn(),
-				keys: jest.fn().mockReturnValue([]),
-			},
-			secrets: {
-				get: jest.fn(),
-				store: jest.fn(),
-				delete: jest.fn(),
-			},
-			subscriptions: [],
-			extension: {
-				packageJSON: { version: "1.0.0" },
-			},
-			globalStorageUri: {
-				fsPath: "/test/storage/path",
-			},
-		} as unknown as vscode.ExtensionContext
-
-		mockOutputChannel = {
-			appendLine: jest.fn(),
-			clear: jest.fn(),
-			dispose: jest.fn(),
-		} as unknown as vscode.OutputChannel
-
-		mockPostMessage = jest.fn()
-		mockWebviewView = {
-			webview: {
-				postMessage: mockPostMessage,
-				html: "",
-				options: {},
-				onDidReceiveMessage: jest.fn(),
-				asWebviewUri: jest.fn(),
-			},
-			visible: true,
-			onDidDispose: jest.fn(),
-			onDidChangeVisibility: jest.fn(),
-		} as unknown as vscode.WebviewView
-
-		provider = new ClineProvider(mockContext, mockOutputChannel)
-	})
-
-	test("handles openProjectMcpSettings message", async () => {
-		await provider.resolveWebviewView(mockWebviewView)
-		const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
-
-		// Mock workspace folders
-		;(vscode.workspace as any).workspaceFolders = [{ uri: { fsPath: "/test/workspace" } }]
-
-		// Mock fs functions
-		const fs = require("fs/promises")
-		fs.mkdir.mockResolvedValue(undefined)
-		fs.writeFile.mockResolvedValue(undefined)
-
-		// Trigger openProjectMcpSettings
-		await messageHandler({
-			type: "openProjectMcpSettings",
-		})
-
-		// Verify directory was created
-		expect(fs.mkdir).toHaveBeenCalledWith(
-			expect.stringContaining(".roo"),
-			expect.objectContaining({ recursive: true }),
-		)
-
-		// Verify file was created with default content
-		expect(fs.writeFile).toHaveBeenCalledWith(
-			expect.stringContaining("mcp.json"),
-			JSON.stringify({ mcpServers: {} }, null, 2),
-		)
-	})
-
-	test("handles openProjectMcpSettings when workspace is not open", async () => {
-		await provider.resolveWebviewView(mockWebviewView)
-		const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
-
-		// Mock no workspace folders
-		;(vscode.workspace as any).workspaceFolders = []
-
-		// Trigger openProjectMcpSettings
-		await messageHandler({
-			type: "openProjectMcpSettings",
-		})
-
-		// Verify error message was shown
-		expect(vscode.window.showErrorMessage).toHaveBeenCalledWith("Please open a project folder first")
-	})
-
-	test("handles openProjectMcpSettings file creation error", async () => {
-		await provider.resolveWebviewView(mockWebviewView)
-		const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
-
-		// Mock workspace folders
-		;(vscode.workspace as any).workspaceFolders = [{ uri: { fsPath: "/test/workspace" } }]
-
-		// Mock fs functions to fail
-		const fs = require("fs/promises")
-		fs.mkdir.mockRejectedValue(new Error("Failed to create directory"))
-
-		// Trigger openProjectMcpSettings
-		await messageHandler({
-			type: "openProjectMcpSettings",
-		})
-
-		// Verify error message was shown
-		expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
-			expect.stringContaining("Failed to create or open .roo/mcp.json"),
-		)
-	})
-})
-
 describe("ContextProxy integration", () => {
 	let provider: ClineProvider
 	let mockContext: vscode.ExtensionContext

+ 42 - 221
src/services/mcp/McpHub.ts

@@ -102,7 +102,6 @@ export class McpHub {
 	private providerRef: WeakRef<ClineProvider>
 	private disposables: vscode.Disposable[] = []
 	private settingsWatcher?: vscode.FileSystemWatcher
-	private projectMcpWatcher?: vscode.FileSystemWatcher
 	private fileWatchers: Map<string, FSWatcher> = new Map()
 	private isDisposed: boolean = false
 	connections: McpConnection[] = []
@@ -111,61 +110,7 @@ export class McpHub {
 	constructor(provider: ClineProvider) {
 		this.providerRef = new WeakRef(provider)
 		this.watchMcpSettingsFile()
-		this.watchProjectMcpFile()
-		this.setupWorkspaceFoldersWatcher()
-		this.initializeGlobalMcpServers()
-		this.initializeProjectMcpServers()
-	}
-
-	public setupWorkspaceFoldersWatcher(): void {
-		// Skip if test environment is detected
-		if (process.env.NODE_ENV === "test" || process.env.JEST_WORKER_ID !== undefined) {
-			return
-		}
-		this.disposables.push(
-			vscode.workspace.onDidChangeWorkspaceFolders(async () => {
-				await this.updateProjectMcpServers()
-				this.watchProjectMcpFile()
-			}),
-		)
-	}
-
-	private watchProjectMcpFile(): void {
-		this.projectMcpWatcher?.dispose()
-
-		this.projectMcpWatcher = vscode.workspace.createFileSystemWatcher("**/.roo/mcp.json", false, false, false)
-
-		this.disposables.push(
-			this.projectMcpWatcher.onDidChange(async () => {
-				await this.updateProjectMcpServers()
-			}),
-			this.projectMcpWatcher.onDidCreate(async () => {
-				await this.updateProjectMcpServers()
-			}),
-			this.projectMcpWatcher.onDidDelete(async () => {
-				await this.cleanupProjectMcpServers()
-			}),
-		)
-
-		this.disposables.push(this.projectMcpWatcher)
-	}
-
-	private async updateProjectMcpServers(): Promise<void> {
-		// Only clean up and initialize project servers, not affecting global servers
-		await this.cleanupProjectMcpServers()
-		await this.initializeProjectMcpServers()
-	}
-
-	private async cleanupProjectMcpServers(): Promise<void> {
-		// Only filter and delete project servers
-		const projectServers = this.connections.filter((conn) => conn.server.source === "project")
-
-		for (const conn of projectServers) {
-			await this.deleteConnection(conn.server.name)
-		}
-
-		// Notify webview of changes after cleanup
-		await this.notifyWebviewOfServerChanges()
+		this.initializeMcpServers()
 	}
 
 	/**
@@ -305,8 +250,7 @@ export class McpHub {
 						return
 					}
 					try {
-						// Only update global servers when global settings change
-						await this.updateServerConnections(result.data.mcpServers || {}, "global")
+						await this.updateServerConnections(result.data.mcpServers || {})
 					} catch (error) {
 						this.showErrorMessage("Failed to process MCP settings change", error)
 					}
@@ -315,9 +259,8 @@ export class McpHub {
 		)
 	}
 
-	private async initializeGlobalMcpServers(): Promise<void> {
+	private async initializeMcpServers(): Promise<void> {
 		try {
-			// Initialize global MCP servers
 			const settingsPath = await this.getMcpSettingsFilePath()
 			const content = await fs.readFile(settingsPath, "utf-8")
 			let config: any
@@ -345,70 +288,21 @@ export class McpHub {
 
 				// Still try to connect with the raw config, but show warnings
 				try {
-					await this.updateServerConnections(config.mcpServers || {}, "global")
+					await this.updateServerConnections(config.mcpServers || {})
 				} catch (error) {
-					this.showErrorMessage("Failed to initialize global MCP servers with raw config", error)
+					this.showErrorMessage("Failed to initialize MCP servers with raw config", error)
 				}
 			}
 		} catch (error) {
-			this.showErrorMessage("Failed to initialize global MCP servers", error)
-		}
-	}
-
-	// Get project-level MCP configuration path
-	private async getProjectMcpPath(): Promise<string | null> {
-		if (!vscode.workspace.workspaceFolders?.length) {
-			return null
-		}
-
-		const workspaceFolder = vscode.workspace.workspaceFolders[0]
-		const projectMcpDir = path.join(workspaceFolder.uri.fsPath, ".roo")
-		const projectMcpPath = path.join(projectMcpDir, "mcp.json")
-
-		try {
-			await fs.access(projectMcpPath)
-			return projectMcpPath
-		} catch {
-			return null
-		}
-	}
-
-	// Initialize project-level MCP servers
-	private async initializeProjectMcpServers(): Promise<void> {
-		const projectMcpPath = await this.getProjectMcpPath()
-		if (!projectMcpPath) {
-			return
-		}
-
-		try {
-			const content = await fs.readFile(projectMcpPath, "utf-8")
-			const config = JSON.parse(content)
-
-			// Validate configuration structure
-			const result = McpSettingsSchema.safeParse(config)
-			if (!result.success) {
-				vscode.window.showErrorMessage(t("common:errors.invalid_mcp_config"))
-				return
-			}
-
-			// Update server connections
-			await this.updateServerConnections(result.data.mcpServers || {}, "project")
-		} catch (error) {
-			console.error("Failed to initialize project MCP servers:", error)
-			vscode.window.showErrorMessage(t("common:errors.failed_initialize_project_mcp", { error }))
+			this.showErrorMessage("Failed to initialize MCP servers", error)
 		}
 	}
 
-	private async connectToServer(
-		name: string,
-		config: z.infer<typeof ServerConfigSchema>,
-		source: "global" | "project" = "global",
-	): Promise<void> {
+	private async connectToServer(name: string, config: z.infer<typeof ServerConfigSchema>): Promise<void> {
 		// Remove existing connection if it exists
 		await this.deleteConnection(name)
 
 		try {
-			// Each MCP server requires its own transport connection and has unique capabilities, configurations, and error handling. Having separate clients also allows proper scoping of resources/tools and independent server management like reconnection.
 			const client = new Client(
 				{
 					name: "Roo Code",
@@ -457,22 +351,15 @@ export class McpHub {
 				const stderrStream = transport.stderr
 				if (stderrStream) {
 					stderrStream.on("data", async (data: Buffer) => {
-						const output = data.toString()
-
-						// Check if this is a startup info message or a real error
-						const isStartupInfo = output.includes("server running") || output.includes("MCP server running")
-
-						if (!isStartupInfo) {
-							// Only log and process real errors, ignore startup info messages
-							console.error(`Server "${name}" stderr:`, output)
-							const connection = this.connections.find((conn) => conn.server.name === name)
-							if (connection) {
-								// NOTE: we do not set server status to "disconnected" because stderr logs do not necessarily mean the server crashed or disconnected
-								this.appendErrorMessage(connection, output)
-								// Only need to update webview right away if it's already disconnected
-								if (connection.server.status === "disconnected") {
-									await this.notifyWebviewOfServerChanges()
-								}
+						const errorOutput = data.toString()
+						console.error(`Server "${name}" stderr:`, errorOutput)
+						const connection = this.connections.find((conn) => conn.server.name === name)
+						if (connection) {
+							// NOTE: we do not set server status to "disconnected" because stderr logs do not necessarily mean the server crashed or disconnected, it could just be informational. In fact when the server first starts up, it immediately logs "<name> server running on stdio" to stderr.
+							this.appendErrorMessage(connection, errorOutput)
+							// Only need to update webview right away if it's already disconnected
+							if (connection.server.status === "disconnected") {
+								await this.notifyWebviewOfServerChanges()
 							}
 						}
 					})
@@ -516,8 +403,6 @@ export class McpHub {
 					config: JSON.stringify(config),
 					status: "connecting",
 					disabled: config.disabled,
-					source,
-					projectPath: source === "project" ? vscode.workspace.workspaceFolders?.[0]?.uri.fsPath : undefined,
 				},
 				client,
 				transport,
@@ -617,17 +502,10 @@ export class McpHub {
 		}
 	}
 
-	async updateServerConnections(
-		newServers: Record<string, any>,
-		source: "global" | "project" = "global",
-	): Promise<void> {
+	async updateServerConnections(newServers: Record<string, any>): Promise<void> {
 		this.isConnecting = true
 		this.removeAllFileWatchers()
-		// Filter connections by source
-		const currentConnections = this.connections.filter(
-			(conn) => conn.server.source === source || (!conn.server.source && source === "global"),
-		)
-		const currentNames = new Set(currentConnections.map((conn) => conn.server.name))
+		const currentNames = new Set(this.connections.map((conn) => conn.server.name))
 		const newNames = new Set(Object.keys(newServers))
 
 		// Delete removed servers
@@ -640,12 +518,7 @@ export class McpHub {
 
 		// Update or add servers
 		for (const [name, config] of Object.entries(newServers)) {
-			// Only consider connections that match the current source
-			const currentConnection = this.connections.find(
-				(conn) =>
-					conn.server.name === name &&
-					(conn.server.source === source || (!conn.server.source && source === "global")),
-			)
+			const currentConnection = this.connections.find((conn) => conn.server.name === name)
 
 			// Validate and transform the config
 			let validatedConfig: z.infer<typeof ServerConfigSchema>
@@ -660,7 +533,7 @@ export class McpHub {
 				// New server
 				try {
 					this.setupFileWatcher(name, validatedConfig)
-					await this.connectToServer(name, validatedConfig, source)
+					await this.connectToServer(name, validatedConfig)
 				} catch (error) {
 					this.showErrorMessage(`Failed to connect to new MCP server ${name}`, error)
 				}
@@ -669,8 +542,8 @@ export class McpHub {
 				try {
 					this.setupFileWatcher(name, validatedConfig)
 					await this.deleteConnection(name)
-					await this.connectToServer(name, validatedConfig, source)
-					console.log(`Reconnected ${source} MCP server with updated config: ${name}`)
+					await this.connectToServer(name, validatedConfig)
+					console.log(`Reconnected MCP server with updated config: ${name}`)
 				} catch (error) {
 					this.showErrorMessage(`Failed to reconnect MCP server ${name}`, error)
 				}
@@ -721,12 +594,10 @@ export class McpHub {
 		if (config) {
 			vscode.window.showInformationMessage(t("common:info.mcp_server_restarting", { serverName }))
 			connection.server.status = "connecting"
-			connection.server.error = "" // Clear any previous error messages
+			connection.server.error = ""
 			await this.notifyWebviewOfServerChanges()
 			await delay(500) // artificial delay to show user that server is restarting
 			try {
-				// Save the original source before deleting the connection
-				const source = connection.server.source || "global"
 				await this.deleteConnection(serverName)
 				// Parse the config to validate it
 				const parsedConfig = JSON.parse(config)
@@ -734,8 +605,8 @@ export class McpHub {
 					// Validate the config
 					const validatedConfig = this.validateServerConfig(parsedConfig, serverName)
 
-					// Try to connect again using validated config and preserve the original source
-					await this.connectToServer(serverName, validatedConfig, source)
+					// Try to connect again using validated config
+					await this.connectToServer(serverName, validatedConfig)
 					vscode.window.showInformationMessage(t("common:info.mcp_server_connected", { serverName }))
 				} catch (validationError) {
 					this.showErrorMessage(`Invalid configuration for MCP server "${serverName}"`, validationError)
@@ -750,49 +621,20 @@ export class McpHub {
 	}
 
 	private async notifyWebviewOfServerChanges(): Promise<void> {
-		// Get global server order from settings file
+		// servers should always be sorted in the order they are defined in the settings file
 		const settingsPath = await this.getMcpSettingsFilePath()
 		const content = await fs.readFile(settingsPath, "utf-8")
 		const config = JSON.parse(content)
-		const globalServerOrder = Object.keys(config.mcpServers || {})
-
-		// Get project server order if available
-		const projectMcpPath = await this.getProjectMcpPath()
-		let projectServerOrder: string[] = []
-		if (projectMcpPath) {
-			try {
-				const projectContent = await fs.readFile(projectMcpPath, "utf-8")
-				const projectConfig = JSON.parse(projectContent)
-				projectServerOrder = Object.keys(projectConfig.mcpServers || {})
-			} catch (error) {
-				console.error("Failed to read project MCP config:", error)
-			}
-		}
-
-		// Sort connections: first global servers in their defined order, then project servers in their defined order
-		const sortedConnections = [...this.connections].sort((a, b) => {
-			const aIsGlobal = a.server.source === "global" || !a.server.source
-			const bIsGlobal = b.server.source === "global" || !b.server.source
-
-			// If both are global or both are project, sort by their respective order
-			if (aIsGlobal && bIsGlobal) {
-				const indexA = globalServerOrder.indexOf(a.server.name)
-				const indexB = globalServerOrder.indexOf(b.server.name)
-				return indexA - indexB
-			} else if (!aIsGlobal && !bIsGlobal) {
-				const indexA = projectServerOrder.indexOf(a.server.name)
-				const indexB = projectServerOrder.indexOf(b.server.name)
-				return indexA - indexB
-			}
-
-			// Global servers come before project servers
-			return aIsGlobal ? -1 : 1
-		})
-
-		// Send sorted servers to webview
+		const serverOrder = Object.keys(config.mcpServers || {})
 		await this.providerRef.deref()?.postMessageToWebview({
 			type: "mcpServers",
-			mcpServers: sortedConnections.map((connection) => connection.server),
+			mcpServers: [...this.connections]
+				.sort((a, b) => {
+					const indexA = serverOrder.indexOf(a.server.name)
+					const indexB = serverOrder.indexOf(b.server.name)
+					return indexA - indexB
+				})
+				.map((connection) => connection.server),
 		})
 	}
 
@@ -914,30 +756,16 @@ export class McpHub {
 
 	public async deleteServer(serverName: string): Promise<void> {
 		try {
-			// Find the connection to determine if it's a global or project server
-			const connection = this.connections.find((conn) => conn.server.name === serverName)
-			const isProjectServer = connection?.server.source === "project"
-
-			// Determine which config file to modify
-			let configPath: string
-			if (isProjectServer) {
-				const projectMcpPath = await this.getProjectMcpPath()
-				if (!projectMcpPath) {
-					throw new Error("Project MCP configuration file not found")
-				}
-				configPath = projectMcpPath
-			} else {
-				configPath = await this.getMcpSettingsFilePath()
-			}
+			const settingsPath = await this.getMcpSettingsFilePath()
 
-			// Ensure the config file exists and is accessible
+			// Ensure the settings file exists and is accessible
 			try {
-				await fs.access(configPath)
+				await fs.access(settingsPath)
 			} catch (error) {
-				throw new Error(`Configuration file not accessible: ${configPath}`)
+				throw new Error("Settings file not accessible")
 			}
 
-			const content = await fs.readFile(configPath, "utf-8")
+			const content = await fs.readFile(settingsPath, "utf-8")
 			const config = JSON.parse(content)
 
 			// Validate the config structure
@@ -958,17 +786,10 @@ export class McpHub {
 					mcpServers: config.mcpServers,
 				}
 
-				await fs.writeFile(configPath, JSON.stringify(updatedConfig, null, 2))
-
-				// Delete the connection
-				await this.deleteConnection(serverName)
+				await fs.writeFile(settingsPath, JSON.stringify(updatedConfig, null, 2))
 
-				// If it's a project server, update project servers, otherwise update global servers
-				if (isProjectServer) {
-					await this.updateProjectMcpServers()
-				} else {
-					await this.updateServerConnections(config.mcpServers)
-				}
+				// Update server connections
+				await this.updateServerConnections(config.mcpServers)
 
 				vscode.window.showInformationMessage(t("common:info.mcp_server_deleted", { serverName }))
 			} else {

+ 1 - 21
src/services/mcp/__tests__/McpHub.test.ts

@@ -7,27 +7,7 @@ import { ServerConfigSchema } from "../McpHub"
 const fs = require("fs/promises")
 const { McpHub } = require("../McpHub")
 
-jest.mock("vscode", () => ({
-	workspace: {
-		createFileSystemWatcher: jest.fn().mockReturnValue({
-			onDidChange: jest.fn(),
-			onDidCreate: jest.fn(),
-			onDidDelete: jest.fn(),
-			dispose: jest.fn(),
-		}),
-		onDidSaveTextDocument: jest.fn(),
-		onDidChangeWorkspaceFolders: jest.fn(),
-		workspaceFolders: [],
-	},
-	window: {
-		showErrorMessage: jest.fn(),
-		showInformationMessage: jest.fn(),
-		showWarningMessage: jest.fn(),
-	},
-	Disposable: {
-		from: jest.fn(),
-	},
-}))
+jest.mock("vscode")
 jest.mock("fs/promises")
 jest.mock("../../../core/webview/ClineProvider")
 

+ 0 - 1
src/shared/WebviewMessage.ts

@@ -63,7 +63,6 @@ export interface WebviewMessage {
 		| "screenshotQuality"
 		| "remoteBrowserHost"
 		| "openMcpSettings"
-		| "openProjectMcpSettings"
 		| "restartMcpServer"
 		| "toggleToolAlwaysAllow"
 		| "toggleMcpServer"

+ 0 - 2
src/shared/mcp.ts

@@ -8,8 +8,6 @@ export type McpServer = {
 	resourceTemplates?: McpResourceTemplate[]
 	disabled?: boolean
 	timeout?: number
-	source?: "global" | "project"
-	projectPath?: string
 }
 
 export type McpTool = {

+ 5 - 29
webview-ui/src/components/mcp/McpView.tsx

@@ -94,25 +94,16 @@ const McpView = ({ onDone }: McpViewProps) => {
 							</div>
 						)}
 
-						{/* Edit Settings Buttons */}
-						<div style={{ marginTop: "10px", width: "100%", display: "flex", gap: "10px" }}>
+						{/* Edit Settings Button */}
+						<div style={{ marginTop: "10px", width: "100%" }}>
 							<VSCodeButton
 								appearance="secondary"
-								style={{ flex: 1 }}
+								style={{ width: "100%" }}
 								onClick={() => {
 									vscode.postMessage({ type: "openMcpSettings" })
 								}}>
 								<span className="codicon codicon-edit" style={{ marginRight: "6px" }}></span>
-								{t("mcp:editGlobalMCP")}
-							</VSCodeButton>
-							<VSCodeButton
-								appearance="secondary"
-								style={{ flex: 1 }}
-								onClick={() => {
-									vscode.postMessage({ type: "openProjectMcpSettings" })
-								}}>
-								<span className="codicon codicon-edit" style={{ marginRight: "6px" }}></span>
-								{t("mcp:editProjectMCP")}
+								{t("mcp:editSettings")}
 							</VSCodeButton>
 						</div>
 					</>
@@ -203,22 +194,7 @@ const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowM
 						style={{ marginRight: "8px" }}
 					/>
 				)}
-				<span style={{ flex: 1 }}>
-					{server.name}
-					{server.source && (
-						<span
-							style={{
-								marginLeft: "8px",
-								padding: "1px 6px",
-								fontSize: "11px",
-								borderRadius: "4px",
-								background: "var(--vscode-badge-background)",
-								color: "var(--vscode-badge-foreground)",
-							}}>
-							{server.source}
-						</span>
-					)}
-				</span>
+				<span style={{ flex: 1 }}>{server.name}</span>
 				<div
 					style={{ display: "flex", alignItems: "center", marginRight: "8px" }}
 					onClick={(e) => e.stopPropagation()}>

+ 1 - 2
webview-ui/src/i18n/locales/ca/mcp.json

@@ -10,8 +10,7 @@
 		"title": "Habilitar creació de servidors MCP",
 		"description": "Quan està habilitat, Roo pot ajudar-te a crear nous servidors MCP mitjançant ordres com \"afegir una nova eina per a...\". Si no necessites crear servidors MCP, pots desactivar això per reduir l'ús de tokens de Roo."
 	},
-	"editGlobalMCP": "Editar MCP Global",
-	"editProjectMCP": "Editar MCP del Projecte",
+	"editSettings": "Editar configuració de MCP",
 	"tool": {
 		"alwaysAllow": "Permetre sempre",
 		"parameters": "Paràmetres",

+ 1 - 2
webview-ui/src/i18n/locales/de/mcp.json

@@ -10,8 +10,7 @@
 		"title": "MCP-Server-Erstellung aktivieren",
 		"description": "Wenn aktiviert, kann Roo Ihnen helfen, neue MCP-Server über Befehle wie \"neues Tool hinzufügen zu...\" zu erstellen. Wenn Sie keine MCP-Server erstellen müssen, können Sie dies deaktivieren, um den Token-Verbrauch von Roo zu reduzieren."
 	},
-	"editGlobalMCP": "Globales MCP bearbeiten",
-	"editProjectMCP": "Projekt-MCP bearbeiten",
+	"editSettings": "MCP-Einstellungen bearbeiten",
 	"tool": {
 		"alwaysAllow": "Immer erlauben",
 		"parameters": "Parameter",

+ 1 - 2
webview-ui/src/i18n/locales/en/mcp.json

@@ -10,8 +10,7 @@
 		"title": "Enable MCP Server Creation",
 		"description": "When enabled, Roo can help you create new MCP servers via commands like \"add a new tool to...\". If you don't need to create MCP servers you can disable this to reduce Roo's token usage."
 	},
-	"editGlobalMCP": "Edit Global MCP",
-	"editProjectMCP": "Edit Project MCP",
+	"editSettings": "Edit MCP Settings",
 	"tool": {
 		"alwaysAllow": "Always allow",
 		"parameters": "Parameters",

+ 1 - 2
webview-ui/src/i18n/locales/es/mcp.json

@@ -10,8 +10,7 @@
 		"title": "Habilitar creación de servidores MCP",
 		"description": "Cuando está habilitado, Roo puede ayudarte a crear nuevos servidores MCP mediante comandos como \"añadir una nueva herramienta para...\". Si no necesitas crear servidores MCP, puedes desactivar esto para reducir el uso de tokens de Roo."
 	},
-	"editGlobalMCP": "Editar MCP Global",
-	"editProjectMCP": "Editar MCP del Proyecto",
+	"editSettings": "Editar configuración de MCP",
 	"tool": {
 		"alwaysAllow": "Permitir siempre",
 		"parameters": "Parámetros",

+ 1 - 2
webview-ui/src/i18n/locales/fr/mcp.json

@@ -10,8 +10,7 @@
 		"title": "Activer la création de serveurs MCP",
 		"description": "Lorsqu'activé, Roo peut vous aider à créer de nouveaux serveurs MCP via des commandes comme \"ajouter un nouvel outil pour...\". Si vous n'avez pas besoin de créer des serveurs MCP, vous pouvez désactiver cette option pour réduire l'utilisation de tokens par Roo."
 	},
-	"editGlobalMCP": "Modifier MCP Global",
-	"editProjectMCP": "Modifier MCP du Projet",
+	"editSettings": "Modifier les paramètres MCP",
 	"tool": {
 		"alwaysAllow": "Toujours autoriser",
 		"parameters": "Paramètres",

+ 1 - 2
webview-ui/src/i18n/locales/hi/mcp.json

@@ -10,8 +10,7 @@
 		"title": "MCP सर्वर निर्माण सक्षम करें",
 		"description": "जब सक्षम होता है, तो Roo आपको \"में नया उपकरण जोड़ें...\" जैसे कमांड के माध्यम से नए MCP सर्वर बनाने में मदद कर सकता है। यदि आपको MCP सर्वर बनाने की आवश्यकता नहीं है, तो आप Roo के token उपयोग को कम करने के लिए इसे अक्षम कर सकते हैं।"
 	},
-	"editGlobalMCP": "वैश्विक MCP संपादित करें",
-	"editProjectMCP": "प्रोजेक्ट MCP संपादित करें",
+	"editSettings": "MCP सेटिंग्स संपादित करें",
 	"tool": {
 		"alwaysAllow": "हमेशा अनुमति दें",
 		"parameters": "पैरामीटर",

+ 1 - 2
webview-ui/src/i18n/locales/it/mcp.json

@@ -10,8 +10,7 @@
 		"title": "Abilita creazione server MCP",
 		"description": "Quando abilitato, Roo può aiutarti a creare nuovi server MCP tramite comandi come \"aggiungi un nuovo strumento per...\". Se non hai bisogno di creare server MCP, puoi disabilitare questa opzione per ridurre l'utilizzo di token da parte di Roo."
 	},
-	"editGlobalMCP": "Modifica MCP Globale",
-	"editProjectMCP": "Modifica MCP del Progetto",
+	"editSettings": "Modifica impostazioni MCP",
 	"tool": {
 		"alwaysAllow": "Consenti sempre",
 		"parameters": "Parametri",

+ 1 - 2
webview-ui/src/i18n/locales/ja/mcp.json

@@ -10,8 +10,7 @@
 		"title": "MCPサーバー作成を有効にする",
 		"description": "有効にすると、Rooは「新しいツールを追加する...」などのコマンドを通じて新しいMCPサーバーの作成を支援できます。MCPサーバーを作成する必要がない場合は、これを無効にしてRooのtoken使用量を減らすことができます。"
 	},
-	"editGlobalMCP": "グローバルMCPを編集",
-	"editProjectMCP": "プロジェクトMCPを編集",
+	"editSettings": "MCP設定を編集",
 	"tool": {
 		"alwaysAllow": "常に許可",
 		"parameters": "パラメータ",

+ 1 - 2
webview-ui/src/i18n/locales/ko/mcp.json

@@ -10,8 +10,7 @@
 		"title": "MCP 서버 생성 활성화",
 		"description": "활성화하면 Roo가 \"새 도구 추가...\"와 같은 명령을 통해 새 MCP 서버를 만드는 데 도움을 줄 수 있습니다. MCP 서버를 만들 필요가 없다면 이 기능을 비활성화하여 Roo의 token 사용량을 줄일 수 있습니다."
 	},
-	"editGlobalMCP": "전역 MCP 편집",
-	"editProjectMCP": "프로젝트 MCP 편집",
+	"editSettings": "MCP 설정 편집",
 	"tool": {
 		"alwaysAllow": "항상 허용",
 		"parameters": "매개변수",

+ 1 - 2
webview-ui/src/i18n/locales/pl/mcp.json

@@ -10,8 +10,7 @@
 		"title": "Włącz tworzenie serwerów MCP",
 		"description": "Po włączeniu, Roo może pomóc w tworzeniu nowych serwerów MCP za pomocą poleceń takich jak \"dodaj nowe narzędzie do...\". Jeśli nie potrzebujesz tworzyć serwerów MCP, możesz to wyłączyć, aby zmniejszyć zużycie tokenów przez Roo."
 	},
-	"editGlobalMCP": "Edytuj globalne MCP",
-	"editProjectMCP": "Edytuj projektowe MCP",
+	"editSettings": "Edytuj ustawienia MCP",
 	"tool": {
 		"alwaysAllow": "Zawsze zezwalaj",
 		"parameters": "Parametry",

+ 1 - 2
webview-ui/src/i18n/locales/pt-BR/mcp.json

@@ -10,8 +10,7 @@
 		"title": "Ativar criação de servidores MCP",
 		"description": "Quando ativado, o Roo pode ajudar você a criar novos servidores MCP por meio de comandos como \"adicionar uma nova ferramenta para...\". Se você não precisar criar servidores MCP, pode desativar isso para reduzir o uso de tokens do Roo."
 	},
-	"editGlobalMCP": "Editar MCP Global",
-	"editProjectMCP": "Editar MCP do Projeto",
+	"editSettings": "Editar configurações do MCP",
 	"tool": {
 		"alwaysAllow": "Sempre permitir",
 		"parameters": "Parâmetros",

+ 1 - 2
webview-ui/src/i18n/locales/tr/mcp.json

@@ -10,8 +10,7 @@
 		"title": "MCP Sunucu Oluşturmayı Etkinleştir",
 		"description": "Etkinleştirildiğinde, Roo \"için yeni bir araç ekle...\" gibi komutlar aracılığıyla yeni MCP sunucuları oluşturmanıza yardımcı olabilir. MCP sunucuları oluşturmanız gerekmiyorsa, Roo'nun token kullanımını azaltmak için bunu devre dışı bırakabilirsiniz."
 	},
-	"editGlobalMCP": "Global MCP'yi Düzenle",
-	"editProjectMCP": "Proje MCP'yi Düzenle",
+	"editSettings": "MCP Ayarlarını Düzenle",
 	"tool": {
 		"alwaysAllow": "Her zaman izin ver",
 		"parameters": "Parametreler",

+ 0 - 2
webview-ui/src/i18n/locales/vi/mcp.json

@@ -10,8 +10,6 @@
 		"title": "Bật tạo máy chủ MCP",
 		"description": "Khi được bật, Roo có thể giúp bạn tạo máy chủ MCP mới thông qua các lệnh như \"thêm công cụ mới để...\". Nếu bạn không cần tạo máy chủ MCP, bạn có thể tắt tính năng này để giảm lượng token mà Roo sử dụng."
 	},
-	"editGlobalMCP": "Chỉnh sửa MCP toàn cục",
-	"editProjectMCP": "Chỉnh sửa MCP dự án",
 	"editSettings": "Chỉnh sửa cài đặt MCP",
 	"tool": {
 		"alwaysAllow": "Luôn cho phép",

+ 0 - 2
webview-ui/src/i18n/locales/zh-CN/mcp.json

@@ -10,8 +10,6 @@
 		"title": "启用 MCP 服务器创建",
 		"description": "启用后,Roo 可以通过诸如\"添加新工具到...\"之类的命令帮助您创建新的 MCP 服务器。如果您不需要创建 MCP 服务器,可以禁用此功能以减少 Roo 的 token 使用量。"
 	},
-	"editGlobalMCP": "编辑全局 MCP",
-	"editProjectMCP": "编辑项目 MCP",
 	"editSettings": "编辑 MCP 设置",
 	"tool": {
 		"alwaysAllow": "始终允许",

+ 0 - 2
webview-ui/src/i18n/locales/zh-TW/mcp.json

@@ -10,8 +10,6 @@
 		"title": "啟用 MCP 伺服器創建",
 		"description": "啟用後,Roo 可以通過如\"新增工具到...\"之類的命令幫助您創建新的 MCP 伺服器。如果您不需要創建 MCP 伺服器,可以停用此功能以減少 Roo 的 token 使用量。"
 	},
-	"editGlobalMCP": "編輯全域 MCP",
-	"editProjectMCP": "編輯專案 MCP",
 	"editSettings": "編輯 MCP 設定",
 	"tool": {
 		"alwaysAllow": "始終允許",