From 26be9469c43f78d162cf3a3ec5500d68dabce23b Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Wed, 19 Feb 2025 18:02:20 -0800 Subject: [PATCH] Add delete button to MCP server --- src/core/webview/ClineProvider.ts | 6 ++++ src/services/mcp/McpHub.ts | 27 +++++++++++++++++ src/shared/WebviewMessage.ts | 1 + .../src/components/common/DangerButton.tsx | 30 +++++++++++++++++++ webview-ui/src/components/mcp/McpView.tsx | 21 +++++++++++++ 5 files changed, 85 insertions(+) create mode 100644 webview-ui/src/components/common/DangerButton.tsx diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 72a93df073..1eb078247f 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -791,6 +791,12 @@ export class ClineProvider implements vscode.WebviewViewProvider { } break } + case "deleteMcpServer": { + if (message.serverName) { + this.mcpHub?.deleteServer(message.serverName) + } + break + } case "fetchLatestMcpServersFromHub": { this.mcpHub?.sendLatestMcpServers() break diff --git a/src/services/mcp/McpHub.ts b/src/services/mcp/McpHub.ts index 9e4a6b9cfb..556d6d8c47 100644 --- a/src/services/mcp/McpHub.ts +++ b/src/services/mcp/McpHub.ts @@ -636,6 +636,33 @@ export class McpHub { } } + public async deleteServer(serverName: string) { + try { + const settingsPath = await this.getMcpSettingsFilePath() + const content = await fs.readFile(settingsPath, "utf-8") + const config = JSON.parse(content) + if (!config.mcpServers || typeof config.mcpServers !== "object") { + config.mcpServers = {} + } + if (config.mcpServers[serverName]) { + delete config.mcpServers[serverName] + const updatedConfig = { + mcpServers: config.mcpServers, + } + await fs.writeFile(settingsPath, JSON.stringify(updatedConfig, null, 2)) + await this.updateServerConnections(config.mcpServers) + vscode.window.showInformationMessage(`Deleted ${serverName} MCP server`) + } else { + vscode.window.showWarningMessage(`${serverName} not found in MCP configuration`) + } + } catch (error) { + vscode.window.showErrorMessage( + `Failed to delete MCP server: ${error instanceof Error ? error.message : String(error)}`, + ) + throw error + } + } + async dispose(): Promise { this.removeAllFileWatchers() for (const connection of this.connections) { diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 87f6785019..e42cb5860b 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -29,6 +29,7 @@ export interface WebviewMessage { | "refreshOpenAiModels" | "openMcpSettings" | "restartMcpServer" + | "deleteMcpServer" | "autoApprovalSettings" | "browserSettings" | "togglePlanActMode" diff --git a/webview-ui/src/components/common/DangerButton.tsx b/webview-ui/src/components/common/DangerButton.tsx new file mode 100644 index 0000000000..572105db01 --- /dev/null +++ b/webview-ui/src/components/common/DangerButton.tsx @@ -0,0 +1,30 @@ +import { VSCodeButton } from "@vscode/webview-ui-toolkit/react" +import styled from "styled-components" + +const StyledButton = styled(VSCodeButton)` + --danger-button-bg: #c42b2b; + --danger-button-hover: #a82424; + --danger-button-active: #8f1f1f; + + background-color: var(--danger-button-bg) !important; + border-color: var(--danger-button-bg) !important; + color: #ffffff !important; + + &:hover { + background-color: var(--danger-button-hover) !important; + border-color: var(--danger-button-hover) !important; + } + + &:active { + background-color: var(--danger-button-active) !important; + border-color: var(--danger-button-active) !important; + } +` + +interface DangerButtonProps extends React.ComponentProps {} + +const DangerButton: React.FC = (props) => { + return +} + +export default DangerButton diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index e154e87c59..0b121a94bd 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -8,6 +8,7 @@ import McpResourceRow from "./McpResourceRow" import McpMarketplaceView from "./marketplace/McpMarketplaceView" import styled from "styled-components" import { getMcpServerDisplayName } from "../../utils/mcp" +import DangerButton from "../common/DangerButton" type McpViewProps = { onDone: () => void @@ -240,6 +241,7 @@ const ServerRow = ({ server }: { server: McpServer }) => { const { mcpMarketplaceCatalog } = useExtensionState() const [isExpanded, setIsExpanded] = useState(false) + const [isDeleting, setIsDeleting] = useState(false) const getStatusColor = () => { switch (server.status) { @@ -265,6 +267,14 @@ const ServerRow = ({ server }: { server: McpServer }) => { }) } + const handleDelete = () => { + setIsDeleting(true) + vscode.postMessage({ + type: "deleteMcpServer", + serverName: server.name, + }) + } + return (
{ }}> {server.status === "connecting" ? "Restarting..." : "Restart Server"} + + + {isDeleting ? "Deleting..." : "Delete Server"} +
) )}