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

Add restart capability to servers

Saoud Rizwan 1 год назад
Родитель
Сommit
6c5db4135e

+ 2 - 2
src/core/webview/ClineProvider.ts

@@ -498,9 +498,9 @@ export class ClineProvider implements vscode.WebviewViewProvider {
 						}
 						break
 					}
-					case "retryMcpServer": {
+					case "restartMcpServer": {
 						try {
-							await this.mcpHub?.retryConnection(message.text!)
+							await this.mcpHub?.restartConnection(message.text!)
 						} catch (error) {
 							console.error(`Failed to retry connection for ${message.text}:`, error)
 						}

+ 20 - 8
src/services/mcp/McpHub.ts

@@ -23,6 +23,7 @@ import {
 } from "../../shared/mcp"
 import { fileExistsAtPath } from "../../utils/fs"
 import { arePathsEqual } from "../../utils/path"
+import delay from "delay"
 
 export type McpConnection = {
 	server: McpServer
@@ -121,7 +122,7 @@ export class McpHub {
 	}
 
 	private async connectToServer(name: string, config: StdioServerParameters): Promise<void> {
-		// Remove existing connection if it exists
+		// Remove existing connection if it exists (should never happen, the connection should be deleted beforehand)
 		this.connections = this.connections.filter((conn) => conn.server.name !== name)
 
 		try {
@@ -176,7 +177,6 @@ export class McpHub {
 					transport,
 				}
 				this.connections.push(connection)
-				await this.notifyWebviewOfServerChanges()
 				return
 			}
 
@@ -219,8 +219,6 @@ export class McpHub {
 			connection.server.tools = await this.fetchToolsList(name)
 			connection.server.resources = await this.fetchResourcesList(name)
 			connection.server.resourceTemplates = await this.fetchResourceTemplatesList(name)
-
-			await this.notifyWebviewOfServerChanges()
 		} catch (error) {
 			// Update status with error
 			const connection = this.connections.find((conn) => conn.server.name === name)
@@ -228,7 +226,6 @@ export class McpHub {
 				connection.server.status = "disconnected"
 				connection.server.error = error instanceof Error ? error.message : String(error)
 			}
-			await this.notifyWebviewOfServerChanges()
 			throw error
 		}
 	}
@@ -281,7 +278,6 @@ export class McpHub {
 				console.error(`Failed to close transport for ${name}:`, error)
 			}
 			this.connections = this.connections.filter((conn) => conn.server.name !== name)
-			await this.notifyWebviewOfServerChanges()
 		}
 	}
 
@@ -321,10 +317,11 @@ export class McpHub {
 			}
 			// If server exists with same config, do nothing
 		}
+		await this.notifyWebviewOfServerChanges()
 		this.isConnecting = false
 	}
 
-	async retryConnection(serverName: string): Promise<void> {
+	async restartConnection(serverName: string): Promise<void> {
 		this.isConnecting = true
 		const provider = this.providerRef.deref()
 		if (!provider) {
@@ -335,6 +332,10 @@ export class McpHub {
 		const connection = this.connections.find((conn) => conn.server.name === serverName)
 		const config = connection?.server.config
 		if (config) {
+			connection.server.status = "connecting"
+			await this.notifyWebviewOfServerChanges()
+			await delay(500) // artificial delay to show user that server is restarting
+			await this.deleteConnection(serverName)
 			// Try to connect again using existing config
 			await this.connectToServer(serverName, JSON.parse(config))
 		}
@@ -344,9 +345,20 @@ export class McpHub {
 	}
 
 	private async notifyWebviewOfServerChanges(): Promise<void> {
+		// 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 serverOrder = Object.keys(config.mcpServers || {})
 		await this.providerRef.deref()?.postMessageToWebview({
 			type: "mcpServers",
-			mcpServers: this.connections.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),
 		})
 	}
 

+ 1 - 1
src/shared/WebviewMessage.ts

@@ -24,7 +24,7 @@ export interface WebviewMessage {
 		| "cancelTask"
 		| "refreshOpenRouterModels"
 		| "openMcpSettings"
-		| "retryMcpServer"
+		| "restartMcpServer"
 	text?: string
 	askResponse?: ClineAskResponse
 	apiConfiguration?: ApiConfiguration

+ 2 - 2
webview-ui/src/components/chat/ChatRow.tsx

@@ -820,12 +820,12 @@ export const ChatRowContent = ({
 											}}
 										/>
 										{useMcpServer.arguments && (
-											<div style={{ marginTop: "6px" }}>
+											<div style={{ marginTop: "8px" }}>
 												<div
 													style={{
 														marginBottom: "4px",
 														opacity: 0.8,
-														fontSize: "11px",
+														fontSize: "12px",
 														textTransform: "uppercase",
 													}}>
 													Arguments

+ 1 - 1
webview-ui/src/components/mcp/McpResourceRow.tsx

@@ -12,7 +12,7 @@ const McpResourceRow = ({ item }: McpResourceRowProps) => {
 		<div
 			key={uri}
 			style={{
-				padding: "3px 0 8px 0",
+				padding: "3px 0",
 			}}>
 			<div
 				style={{

+ 1 - 1
webview-ui/src/components/mcp/McpToolRow.tsx

@@ -9,7 +9,7 @@ const McpToolRow = ({ tool }: McpToolRowProps) => {
 		<div
 			key={tool.name}
 			style={{
-				padding: "3px 0 8px 0",
+				padding: "3px 0",
 			}}>
 			<div style={{ display: "flex" }}>
 				<span className="codicon codicon-symbol-method" style={{ marginRight: "6px" }}></span>

+ 18 - 8
webview-ui/src/components/mcp/McpView.tsx

@@ -150,9 +150,9 @@ const ServerRow = ({ server }: { server: McpServer }) => {
 		}
 	}
 
-	const handleRetry = () => {
+	const handleRestart = () => {
 		vscode.postMessage({
-			type: "retryMcpServer",
+			type: "restartMcpServer",
 			text: server.name,
 		})
 	}
@@ -196,9 +196,11 @@ const ServerRow = ({ server }: { server: McpServer }) => {
 						borderRadius: "0 0 4px 4px",
 					}}>
 					<div style={{ color: "var(--vscode-testing-iconFailed)", marginBottom: "8px" }}>{server.error}</div>
-					<VSCodeButton appearance="secondary" onClick={handleRetry}>
-						<span className="codicon codicon-debug-restart" style={{ marginRight: "6px" }}></span>
-						Retry Connection
+					<VSCodeButton
+						appearance="secondary"
+						onClick={handleRestart}
+						disabled={server.status === "connecting"}>
+						{server.status === "connecting" ? "Retrying..." : "Retry Connection"}
 					</VSCodeButton>
 				</div>
 			) : (
@@ -206,7 +208,7 @@ const ServerRow = ({ server }: { server: McpServer }) => {
 					<div
 						style={{
 							background: "var(--vscode-textCodeBlock-background)",
-							padding: "0 12px 0 12px",
+							padding: "0 12px 12px 12px",
 							fontSize: "13px",
 							borderRadius: "0 0 4px 4px",
 						}}>
@@ -217,7 +219,7 @@ const ServerRow = ({ server }: { server: McpServer }) => {
 							<VSCodePanelView id="tools-view">
 								{server.tools && server.tools.length > 0 ? (
 									<div
-										style={{ display: "flex", flexDirection: "column", gap: "6px", width: "100%" }}>
+										style={{ display: "flex", flexDirection: "column", gap: "8px", width: "100%" }}>
 										{server.tools.map((tool) => (
 											<McpToolRow key={tool.name} tool={tool} />
 										))}
@@ -233,7 +235,7 @@ const ServerRow = ({ server }: { server: McpServer }) => {
 								{(server.resources && server.resources.length > 0) ||
 								(server.resourceTemplates && server.resourceTemplates.length > 0) ? (
 									<div
-										style={{ display: "flex", flexDirection: "column", gap: "3px", width: "100%" }}>
+										style={{ display: "flex", flexDirection: "column", gap: "8px", width: "100%" }}>
 										{[...(server.resourceTemplates || []), ...(server.resources || [])].map(
 											(item) => (
 												<McpResourceRow
@@ -250,6 +252,14 @@ const ServerRow = ({ server }: { server: McpServer }) => {
 								)}
 							</VSCodePanelView>
 						</VSCodePanels>
+
+						<VSCodeButton
+							appearance="secondary"
+							onClick={handleRestart}
+							disabled={server.status === "connecting"}
+							style={{ width: "calc(100% - 14px)", margin: "0 7px 3px 7px" }}>
+							{server.status === "connecting" ? "Restarting..." : "Restart Server"}
+						</VSCodeButton>
 					</div>
 				)
 			)}