diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 2f3cf4ed540..43ba3a90846 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -941,6 +941,22 @@ export class ClineProvider implements vscode.WebviewViewProvider { } break } + case "deleteMcpServer": { + if (!message.serverName) { + break + } + + try { + this.outputChannel.appendLine(`Attempting to delete MCP server: ${message.serverName}`) + await this.mcpHub?.deleteServer(message.serverName) + this.outputChannel.appendLine(`Successfully deleted MCP server: ${message.serverName}`) + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + this.outputChannel.appendLine(`Failed to delete MCP server: ${errorMessage}`) + // Error messages are already handled by McpHub.deleteServer + } + break + } case "restartMcpServer": { try { await this.mcpHub?.restartConnection(message.text!) diff --git a/src/services/mcp/McpHub.ts b/src/services/mcp/McpHub.ts index 209df3dbe68..6e3f39fa7db 100644 --- a/src/services/mcp/McpHub.ts +++ b/src/services/mcp/McpHub.ts @@ -576,6 +576,59 @@ export class McpHub { } } + public async deleteServer(serverName: string): Promise { + try { + const settingsPath = await this.getMcpSettingsFilePath() + + // Ensure the settings file exists and is accessible + try { + await fs.access(settingsPath) + } catch (error) { + throw new Error("Settings file not accessible") + } + + const content = await fs.readFile(settingsPath, "utf-8") + const config = JSON.parse(content) + + // Validate the config structure + if (!config || typeof config !== "object") { + throw new Error("Invalid config structure") + } + + if (!config.mcpServers || typeof config.mcpServers !== "object") { + config.mcpServers = {} + } + + // Remove the server from the settings + if (config.mcpServers[serverName]) { + delete config.mcpServers[serverName] + + // Write the entire config back + const updatedConfig = { + mcpServers: config.mcpServers, + } + + await fs.writeFile(settingsPath, JSON.stringify(updatedConfig, null, 2)) + + // Update server connections + await this.updateServerConnections(config.mcpServers) + + vscode.window.showInformationMessage(`Deleted MCP server: ${serverName}`) + } else { + vscode.window.showWarningMessage(`Server "${serverName}" not found in configuration`) + } + } catch (error) { + console.error("Failed to delete MCP server:", error) + if (error instanceof Error) { + console.error("Error details:", error.message, error.stack) + } + vscode.window.showErrorMessage( + `Failed to delete MCP server: ${error instanceof Error ? error.message : String(error)}`, + ) + throw error + } + } + async readResource(serverName: string, uri: string): Promise { const connection = this.connections.find((conn) => conn.server.name === serverName) if (!connection) { diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 4dde38dfcde..106e6d243b9 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -93,6 +93,7 @@ export interface WebviewMessage { | "openCustomModesSettings" | "checkpointDiff" | "checkpointRestore" + | "deleteMcpServer" | "maxOpenTabsContext" text?: string disabled?: boolean diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index b5dd59c8282..adb6b47343d 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -13,6 +13,7 @@ import { McpServer } from "../../../../src/shared/mcp" import McpToolRow from "./McpToolRow" import McpResourceRow from "./McpResourceRow" import McpEnabledToggle from "./McpEnabledToggle" +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "../ui/dialog" type McpViewProps = { onDone: () => void @@ -129,6 +130,7 @@ const McpView = ({ onDone }: McpViewProps) => { // Server Row Component const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowMcp?: boolean }) => { const [isExpanded, setIsExpanded] = useState(false) + const [showDeleteConfirm, setShowDeleteConfirm] = useState(false) const [timeoutValue, setTimeoutValue] = useState(() => { const configTimeout = JSON.parse(server.config)?.timeout return configTimeout ?? 60 // Default 1 minute (60 seconds) @@ -179,6 +181,14 @@ const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowM }) } + const handleDelete = () => { + vscode.postMessage({ + type: "deleteMcpServer", + serverName: server.name, + }) + setShowDeleteConfirm(false) + } + return (
e.stopPropagation()}> + setShowDeleteConfirm(true)} + style={{ marginRight: "8px" }}> + + ) )} + + {/* Delete Confirmation Dialog */} + + + + Delete MCP Server + + Are you sure you want to delete the MCP server "{server.name}"? This action cannot be + undone. + + + + setShowDeleteConfirm(false)}> + Cancel + + + Delete + + + +
) }