Added MCP server deletion with confirmation dialog

- Added delete button with trash icon to MCP server rows
- Implemented confirmation dialog using existing Dialog component
- Added "deleteMcpServer" message type to WebviewMessage interface
- Added handler in ClineProvider to process deletion:
  - Removes server from MCP settings file
  - Closes server connection
  - Updates UI to reflect changes
- Added user feedback with success/error notifications
- Ensured consistent modal design with rest of application
This commit is contained in:
Roo Code 2025-02-18 11:19:08 -07:00
parent 9df50b4c8d
commit 7bb8edf720
3 changed files with 111 additions and 0 deletions

View file

@ -941,6 +941,79 @@ export class ClineProvider implements vscode.WebviewViewProvider {
}
break
}
case "deleteMcpServer": {
if (!message.serverName) {
break
}
try {
this.outputChannel.appendLine(`Attempting to delete MCP server: ${message.serverName}`)
const settingsPath = await this.mcpHub?.getMcpSettingsFilePath()
if (!settingsPath) {
throw new Error("Could not get MCP settings file path")
}
// Ensure the settings file exists and is accessible
try {
await fs.access(settingsPath)
} catch (error) {
this.outputChannel.appendLine("Settings file not accessible")
throw new Error("Settings file not accessible")
}
this.outputChannel.appendLine(`Reading MCP settings from: ${settingsPath}`)
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[message.serverName]) {
this.outputChannel.appendLine(
`Removing server ${message.serverName} from configuration`,
)
delete config.mcpServers[message.serverName]
// Write the entire config back
const updatedConfig = {
mcpServers: config.mcpServers,
}
await fs.writeFile(settingsPath, JSON.stringify(updatedConfig, null, 2))
// Update server connections through McpHub
this.outputChannel.appendLine("Updating server connections")
await this.mcpHub?.updateServerConnections(config.mcpServers)
this.outputChannel.appendLine(`Successfully deleted MCP server: ${message.serverName}`)
vscode.window.showInformationMessage(`Deleted MCP server: ${message.serverName}`)
} else {
this.outputChannel.appendLine(`Server ${message.serverName} not found in configuration`)
vscode.window.showWarningMessage(
`Server "${message.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)
}
const errorMessage = error instanceof Error ? error.message : String(error)
this.outputChannel.appendLine(`Failed to delete MCP server: ${errorMessage}`)
vscode.window.showErrorMessage(
`Failed to delete MCP server: ${error instanceof Error ? error.message : String(error)}`,
)
}
break
}
case "restartMcpServer": {
try {
await this.mcpHub?.restartConnection(message.text!)

View file

@ -93,6 +93,7 @@ export interface WebviewMessage {
| "openCustomModesSettings"
| "checkpointDiff"
| "checkpointRestore"
| "deleteMcpServer"
| "maxOpenTabsContext"
text?: string
disabled?: boolean

View file

@ -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 (
<div style={{ marginBottom: "10px" }}>
<div
@ -202,6 +212,12 @@ const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowM
<div
style={{ display: "flex", alignItems: "center", marginRight: "8px" }}
onClick={(e) => e.stopPropagation()}>
<VSCodeButton
appearance="icon"
onClick={() => setShowDeleteConfirm(true)}
style={{ marginRight: "8px" }}>
<span className="codicon codicon-trash" style={{ fontSize: "14px" }}></span>
</VSCodeButton>
<VSCodeButton
appearance="icon"
onClick={handleRestart}
@ -393,6 +409,27 @@ const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowM
</div>
)
)}
{/* Delete Confirmation Dialog */}
<Dialog open={showDeleteConfirm} onOpenChange={setShowDeleteConfirm}>
<DialogContent>
<DialogHeader>
<DialogTitle>Delete MCP Server</DialogTitle>
<DialogDescription>
Are you sure you want to delete the MCP server "{server.name}"? This action cannot be
undone.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<VSCodeButton appearance="secondary" onClick={() => setShowDeleteConfirm(false)}>
Cancel
</VSCodeButton>
<VSCodeButton appearance="primary" onClick={handleDelete}>
Delete
</VSCodeButton>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
)
}