Repository: cline/cline
Pull request #3606: Migrate restartMcpServer protobus
Author: login: celestial-vault
Created: 2025-05-18T05:43:41Z | Merged: 2025-05-19T00:13:19Z

### Description

Migrate the "restartMcpServer" message to protobus

### Test Procedure

Tested that it restarts as before and handles loading state

Tested that the mcp server still works afterwards

### Type of Change

<!-- Put an 'x' in all boxes that apply -->

-   [ ] 🐛 Bug fix (non-breaking change which fixes an issue)
-   [ ] ✨ New feature (non-breaking change which adds functionality)
-   [ ] 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
-   [x] ♻️ Refactor Changes
-   [ ] 💅 Cosmetic Changes
-   [ ] 📚 Documentation update
-   [ ] 🏃 Workflow Changes

### Pre-flight Checklist

<!-- Put an 'x' in all boxes that apply -->

-   [x] Changes are limited to a single feature, bugfix or chore (split larger changes into separate PRs)
-   [x] Tests are passing (`npm test`) and code is formatted and linted (`npm run format && npm run lint`)
-   [x] I have created a changeset using `npm run changeset` (required for user-facing changes)
-   [x] I have reviewed [contributor guidelines](https://github.com/cline/cline/blob/main/CONTRIBUTING.md)

### Screenshots

<!-- For UI changes, add screenshots here -->

### Additional Notes

<!-- Add any additional notes for reviewers -->

<!-- ELLIPSIS_HIDDEN -->


----

> [!IMPORTANT]
> Migrate `restartMcpServer` to protobus, updating RPC methods and UI components to support the change.
> 
>   - **Behavior**:
>     - Migrate `restartMcpServer` to protobus in `mcp.proto`.
>     - Add `restartMcpServer` RPC method to `McpService` in `mcp.proto`.
>     - Update `ServerRow.tsx` to use `McpServiceClient.restartMcpServer()` for server restarts.
>   - **Controller**:
>     - Remove `restartMcpServer` case from `handleWebviewMessage()` in `index.ts`.
>   - **McpHub**:
>     - Add `restartConnectionRPC()` to handle server restarts via RPC in `McpHub.ts`.
>     - Modify `connectToServerRPC()` to support new connection logic in `McpHub.ts`.
>   - **Methods**:
>     - Register `restartMcpServer` in `methods.ts`.
> 
> <sup>This description was created by </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=cline%2Fcline&utm_source=github&utm_medium=referral)<sup> for d2fa53cd892ce84f5add02a43c0089126f80a62a. You can [customize](https://app.ellipsis.dev/cline/settings/summaries) this summary. It will automatically update as commits are pushed.</sup>


<!-- ELLIPSIS_HIDDEN -->


Diff:
diff --git a/.changeset/six-candles-check.md b/.changeset/six-candles-check.md
new file mode 100644
index 00000000000..97db6092c5c
--- /dev/null
+++ b/.changeset/six-candles-check.md
@@ -0,0 +1,5 @@
+---
+"claude-dev": minor
+---
+
+Migrate restartMcpServer to protobus
diff --git a/proto/mcp.proto b/proto/mcp.proto
index d39edc49e56..0625dc313a3 100644
--- a/proto/mcp.proto
+++ b/proto/mcp.proto
@@ -11,6 +11,7 @@ service McpService {
   rpc updateMcpTimeout(UpdateMcpTimeoutRequest) returns (McpServers);
   rpc addRemoteMcpServer(AddRemoteMcpServerRequest) returns (McpServers);
   rpc downloadMcp(StringRequest) returns (Empty);
+  rpc restartMcpServer(StringRequest) returns (McpServers);
 }
 
 message ToggleMcpServerRequest {
diff --git a/src/core/controller/index.ts b/src/core/controller/index.ts
index ce3a4be5d89..cb6861d7d60 100644
--- a/src/core/controller/index.ts
+++ b/src/core/controller/index.ts
@@ -499,14 +499,6 @@ export class Controller {
 				this.refreshTotalTasksSize()
 				break
 			}
-			case "restartMcpServer": {
-				try {
-					await this.mcpHub?.restartConnection(message.text!)
-				} catch (error) {
-					console.error(`Failed to retry connection for ${message.text}:`, error)
-				}
-				break
-			}
 			case "deleteMcpServer": {
 				if (message.serverName) {
 					this.mcpHub?.deleteServer(message.serverName)
diff --git a/src/core/controller/mcp/methods.ts b/src/core/controller/mcp/methods.ts
index 78be1ff9cbd..3f7fd416d13 100644
--- a/src/core/controller/mcp/methods.ts
+++ b/src/core/controller/mcp/methods.ts
@@ -5,6 +5,7 @@
 import { registerMethod } from "./index"
 import { addRemoteMcpServer } from "./addRemoteMcpServer"
 import { downloadMcp } from "./downloadMcp"
+import { restartMcpServer } from "./restartMcpServer"
 import { toggleMcpServer } from "./toggleMcpServer"
 import { updateMcpTimeout } from "./updateMcpTimeout"
 
@@ -13,6 +14,7 @@ export function registerAllMethods(): void {
 	// Register each method with the registry
 	registerMethod("addRemoteMcpServer", addRemoteMcpServer)
 	registerMethod("downloadMcp", downloadMcp)
+	registerMethod("restartMcpServer", restartMcpServer)
 	registerMethod("toggleMcpServer", toggleMcpServer)
 	registerMethod("updateMcpTimeout", updateMcpTimeout)
 }
diff --git a/src/core/controller/mcp/restartMcpServer.ts b/src/core/controller/mcp/restartMcpServer.ts
new file mode 100644
index 00000000000..d0490f868fb
--- /dev/null
+++ b/src/core/controller/mcp/restartMcpServer.ts
@@ -0,0 +1,24 @@
+import type { McpServers } from "@shared/proto/mcp"
+import type { Controller } from "../index"
+import { convertMcpServersToProtoMcpServers } from "@shared/proto-conversions/mcp/mcp-server-conversion"
+import { StringRequest } from "@/shared/proto/common"
+
+/**
+ * Restarts an MCP server connection
+ * @param controller The controller instance
+ * @param request The request containing the server name
+ * @returns The updated list of MCP servers
+ */
+export async function restartMcpServer(controller: Controller, request: StringRequest): Promise<McpServers> {
+	try {
+		const mcpServers = await controller.mcpHub?.restartConnectionRPC(request.value)
+
+		// Convert from McpServer[] to ProtoMcpServer[] ensuring all required fields are set
+		const protoServers = convertMcpServersToProtoMcpServers(mcpServers)
+
+		return { mcpServers: protoServers }
+	} catch (error) {
+		console.error(`Failed to restart MCP server ${request.value}:`, error)
+		throw error
+	}
+}
diff --git a/src/services/mcp/McpHub.ts b/src/services/mcp/McpHub.ts
index 4191d6906cb..f70213478fe 100644
--- a/src/services/mcp/McpHub.ts
+++ b/src/services/mcp/McpHub.ts
@@ -103,6 +103,7 @@ export class McpHub {
 
 	getServers(): McpServer[] {
 		// Only return enabled servers
+
 		return this.connections.filter((conn) => !conn.server.disabled).map((conn) => conn.server)
 	}
 
@@ -180,6 +181,120 @@ export class McpHub {
 		}
 	}
 
+	private async connectToServerRPC(
+		name: string,
+		config: z.infer<typeof StdioConfigSchema> | z.infer<typeof SseConfigSchema>,
+	): Promise<void> {
+		// 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 {
+			// 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: "Cline",
+					version: this.clientVersion,
+				},
+				{
+					capabilities: {},
+				},
+			)
+
+			let transport: StdioClientTransport | SSEClientTransport
+
+			if (config.transportType === "sse") {
+				transport = new SSEClientTransport(new URL(config.url), {})
+			} else {
+				transport = new StdioClientTransport({
+					command: config.command,
+					args: config.args,
+					env: {
+						...config.env,
+						...(process.env.PATH ? { PATH: process.env.PATH } : {}),
+						// ...(process.env.NODE_PATH ? { NODE_PATH: process.env.NODE_PATH } : {}),
+					},
+					stderr: "pipe", // necessary for stderr to be available
+				})
+			}
+
+			transport.onerror = async (error) => {
+				console.error(`Transport error for "${name}":`, error)
+				const connection = this.connections.find((conn) => conn.server.name === name)
+				if (connection) {
+					connection.server.status = "disconnected"
+					this.appendErrorMessage(connection, error.message)
+				}
+			}
+
+			transport.onclose = async () => {
+				const connection = this.connections.find((conn) => conn.server.name === name)
+				if (connection) {
+					connection.server.status = "disconnected"
+				}
+			}
+
+			const connection: McpConnection = {
+				server: {
+					name,
+					config: JSON.stringify(config),
+					status: "connecting",
+					disabled: config.disabled,
+				},
+				client,
+				transport,
+			}
+			this.connections.push(connection)
+
+			if (config.transportType === "stdio") {
+				// transport.stderr is only available after the process has been started. However we can't start it separately from the .connect() call because it also starts the transport. And we can't place this after the connect call since we need to capture the stderr stream before the connection is established, in order to capture errors during the connection process.
+				// As a workaround, we start the transport ourselves, and then monkey-patch the start method to no-op so that .connect() doesn't try to start it again.
+				await transport.start()
+				const stderrStream = (transport as StdioClientTransport).stderr
+				if (stderrStream) {
+					stderrStream.on("data", async (data: Buffer) => {
+						const output = data.toString()
+						// Check if output contains INFO level log
+						const isInfoLog = !/\berror\b/i.test(output)
+
+						if (isInfoLog) {
+							// Log normal informational messages
+							console.info(`Server "${name}" info:`, output)
+						} else {
+							// Treat as error log
+							console.error(`Server "${name}" stderr:`, output)
+							const connection = this.connections.find((conn) => conn.server.name === name)
+							if (connection) {
+								this.appendErrorMessage(connection, output)
+							}
+						}
+					})
+				} else {
+					console.error(`No stderr stream for ${name}`)
+				}
+				transport.start = async () => {} // No-op now, .connect() won't fail
+			}
+
+			// Connect
+			await client.connect(transport)
+
+			connection.server.status = "connected"
+			connection.server.error = ""
+
+			// Initial fetch of tools and resources
+			connection.server.tools = await this.fetchToolsList(name)
+			connection.server.resources = await this.fetchResourcesList(name)
+			connection.server.resourceTemplates = await this.fetchResourceTemplatesList(name)
+		} catch (error) {
+			// Update status with error
+			const connection = this.connections.find((conn) => conn.server.name === name)
+			if (connection) {
+				connection.server.status = "disconnected"
+				this.appendErrorMessage(connection, error instanceof Error ? error.message : String(error))
+			}
+			throw error
+		}
+	}
+
 	private async connectToServer(
 		name: string,
 		config: z.infer<typeof StdioConfigSchema> | z.infer<typeof SseConfigSchema>,
@@ -494,6 +609,36 @@ export class McpHub {
 		this.fileWatchers.clear()
 	}
 
+	async restartConnectionRPC(serverName: string): Promise<McpServer[]> {
+		this.isConnecting = true
+
+		// Get existing connection and update its status
+		const connection = this.connections.find((conn) => conn.server.name === serverName)
+		const inMemoryConfig = connection?.server.config
+		if (inMemoryConfig) {
+			connection.server.status = "connecting"
+			connection.server.error = ""
+			await setTimeoutPromise(500) // artificial delay to show user that server is restarting
+			try {
+				await this.deleteConnection(serverName)
+				// Try to connect again using existing config
+				await this.connectToServerRPC(serverName, JSON.parse(inMemoryConfig))
+			} catch (error) {
+				console.error(`Failed to restart connection for ${serverName}:`, error)
+			}
+		}
+
+		this.isConnecting = false
+
+		const config = await this.readAndValidateMcpSettingsFile()
+		if (!config) {
+			throw new Error("Failed to read or validate MCP settings")
+		}
+
+		const serverOrder = Object.keys(config.mcpServers || {})
+		return this.getSortedMcpServers(serverOrder)
+	}
+
 	async restartConnection(serverName: string): Promise<void> {
 		this.isConnecting = true
 
diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts
index 790e8830e33..c66638fc660 100644
--- a/src/shared/WebviewMessage.ts
+++ b/src/shared/WebviewMessage.ts
@@ -21,7 +21,6 @@ export interface WebviewMessage {
 		| "showChatView"
 		| "refreshClineRules"
 		| "openMcpSettings"
-		| "restartMcpServer"
 		| "deleteMcpServer"
 		| "autoApprovalSettings"
 		| "browserRelaunchResult"
diff --git a/src/shared/proto/file.ts b/src/shared/proto/file.ts
index bd4a3bb4b9a..644ab6d5c56 100644
--- a/src/shared/proto/file.ts
+++ b/src/shared/proto/file.ts
@@ -915,15 +915,6 @@ export const FileServiceDefinition = {
 			responseStream: false,
 			options: {},
 		},
-		/** Select images from the file system and return as data URLs */
-		selectImages: {
-			name: "selectImages",
-			requestType: EmptyRequest,
-			requestStream: false,
-			responseType: StringArray,
-			responseStream: false,
-			options: {},
-		},
 		/** Opens an image in the system viewer */
 		openImage: {
 			name: "openImage",
@@ -960,6 +951,15 @@ export const FileServiceDefinition = {
 			responseStream: false,
 			options: {},
 		},
+		/** Select images from the file system and return as data URLs */
+		selectImages: {
+			name: "selectImages",
+			requestType: EmptyRequest,
+			requestStream: false,
+			responseType: StringArray,
+			responseStream: false,
+			options: {},
+		},
 		/** Convert URIs to workspace-relative paths */
 		getRelativePaths: {
 			name: "getRelativePaths",
diff --git a/src/shared/proto/mcp.ts b/src/shared/proto/mcp.ts
index 35ee8ad6a36..244961045ba 100644
--- a/src/shared/proto/mcp.ts
+++ b/src/shared/proto/mcp.ts
@@ -1012,6 +1012,14 @@ export const McpServiceDefinition = {
 			responseStream: false,
 			options: {},
 		},
+		restartMcpServer: {
+			name: "restartMcpServer",
+			requestType: StringRequest,
+			requestStream: false,
+			responseType: McpServers,
+			responseStream: false,
+			options: {},
+		},
 	},
 } as const
 
diff --git a/src/standalone/server-setup.ts b/src/standalone/server-setup.ts
index 842d1fd5773..ea348377290 100644
--- a/src/standalone/server-setup.ts
+++ b/src/standalone/server-setup.ts
@@ -20,11 +20,11 @@ import { checkpointRestore } from "../core/controller/checkpoints/checkpointRest
 
 // File Service
 import { openFile } from "../core/controller/file/openFile"
-import { selectImages } from "../core/controller/file/selectImages"
 import { openImage } from "../core/controller/file/openImage"
 import { deleteRuleFile } from "../core/controller/file/deleteRuleFile"
 import { createRuleFile } from "../core/controller/file/createRuleFile"
 import { searchCommits } from "../core/controller/file/searchCommits"
+import { selectImages } from "../core/controller/file/selectImages"
 import { getRelativePaths } from "../core/controller/file/getRelativePaths"
 import { searchFiles } from "../core/controller/file/searchFiles"
 
@@ -33,6 +33,7 @@ import { toggleMcpServer } from "../core/controller/mcp/toggleMcpServer"
 import { updateMcpTimeout } from "../core/controller/mcp/updateMcpTimeout"
 import { addRemoteMcpServer } from "../core/controller/mcp/addRemoteMcpServer"
 import { downloadMcp } from "../core/controller/mcp/downloadMcp"
+import { restartMcpServer } from "../core/controller/mcp/restartMcpServer"
 
 // Models Service
 import { getOllamaModels } from "../core/controller/models/getOllamaModels"
@@ -96,11 +97,11 @@ export function addServices(
 	// File Service
 	server.addService(proto.cline.FileService.service, {
 		openFile: wrapper(openFile, controller),
-		selectImages: wrapper(selectImages, controller),
 		openImage: wrapper(openImage, controller),
 		deleteRuleFile: wrapper(deleteRuleFile, controller),
 		createRuleFile: wrapper(createRuleFile, controller),
 		searchCommits: wrapper(searchCommits, controller),
+		selectImages: wrapper(selectImages, controller),
 		getRelativePaths: wrapper(getRelativePaths, controller),
 		searchFiles: wrapper(searchFiles, controller),
 	})
@@ -111,6 +112,7 @@ export function addServices(
 		updateMcpTimeout: wrapper(updateMcpTimeout, controller),
 		addRemoteMcpServer: wrapper(addRemoteMcpServer, controller),
 		downloadMcp: wrapper(downloadMcp, controller),
+		restartMcpServer: wrapper(restartMcpServer, controller),
 	})
 
 	// Models Service
diff --git a/webview-ui/src/components/mcp/configuration/tabs/installed/server-row/ServerRow.tsx b/webview-ui/src/components/mcp/configuration/tabs/installed/server-row/ServerRow.tsx
index 91b00157889..b00a512e8b0 100644
--- a/webview-ui/src/components/mcp/configuration/tabs/installed/server-row/ServerRow.tsx
+++ b/webview-ui/src/components/mcp/configuration/tabs/installed/server-row/ServerRow.tsx
@@ -19,6 +19,7 @@ import { useExtensionState } from "@/context/ExtensionStateContext"
 import { McpServiceClient } from "@/services/grpc-client"
 import { convertProtoMcpServersToMcpServers } from "@shared/proto-conversions/mcp/mcp-server-conversion"
 import { McpServers, UpdateMcpTimeoutRequest } from "@shared/proto/mcp"
+import { StringRequest } from "@shared/proto/common"
 // constant JSX.Elements
 const TimeoutOptions = [
 	{ value: "30", label: "30 seconds" },
@@ -46,6 +47,7 @@ const ServerRow = ({
 
 	const [isExpanded, setIsExpanded] = useState(false)
 	const [isDeleting, setIsDeleting] = useState(false)
+	const [isRestarting, setIsRestarting] = useState(false)
 
 	const getStatusColor = useCallback((status: McpServer["status"]) => {
 		switch (status) {
@@ -93,10 +95,24 @@ const ServerRow = ({
 	}
 
 	const handleRestart = () => {
-		vscode.postMessage({
-			type: "restartMcpServer",
-			text: server.name,
-		})
+		// Set local state to show "connecting" status
+		setIsRestarting(true)
+
+		// Make the gRPC call
+		McpServiceClient.restartMcpServer({
+			value: server.name,
+		} as StringRequest)
+			.then((response: McpServers) => {
+				// Update with the final state from the server
+				const mcpServers = convertProtoMcpServersToMcpServers(response.mcpServers)
+				setMcpServers(mcpServers)
+				setIsRestarting(false)
+			})
+			.catch((error) => {
+				// Reset the restarting state
+				setIsRestarting(false)
+				console.error("Error restarting MCP server", error)
+			})
 	}
 
 	const handleDelete = () => {
@@ -171,7 +187,7 @@ const ServerRow = ({
 								e.stopPropagation()
 								handleRestart()
 							}}
-							disabled={server.status === "connecting"}>
+							disabled={server.status === "connecting" || isRestarting}>
 							<span className="codicon codicon-sync"></span>
 						</VSCodeButton>
 						{hasTrashIcon && (
@@ -267,7 +283,7 @@ const ServerRow = ({
 							width: "calc(100% - 20px)",
 							margin: "0 10px 10px 10px",
 						}}>
-						{server.status === "connecting" ? "Retrying..." : "Retry Connection"}
+						{server.status === "connecting" || isRestarting ? "Retrying..." : "Retry Connection"}
 					</VSCodeButton>
 
 					<DangerButton
@@ -363,12 +379,12 @@ const ServerRow = ({
 						<VSCodeButton
 							appearance="secondary"
 							onClick={handleRestart}
-							disabled={server.status === "connecting"}
+							disabled={server.status === "connecting" || isRestarting}
 							style={{
 								width: "calc(100% - 14px)",
 								margin: "0 7px 3px 7px",
 							}}>
-							{server.status === "connecting" ? "Restarting..." : "Restart Server"}
+							{server.status === "connecting" || isRestarting ? "Restarting..." : "Restart Server"}
 						</VSCodeButton>
 
 						<DangerButton


Reviews:
user: login: celestial-vault
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T05:46:18Z
user: login: celestial-vault
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T05:46:36Z
user: login: celestial-vault
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T05:47:17Z
user: login: pashpashpash
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T07:18:48Z
user: login: canvrno
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T17:02:21Z
user: login: celestial-vault
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T17:27:31Z
user: login: canvrno
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T17:35:20Z
user: login: celestial-vault
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T17:57:53Z
user: login: canvrno
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T18:18:03Z
user: login: pashpashpash
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T18:53:23Z
user: login: canvrno
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T21:57:41Z
user: login: pashpashpash
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T22:20:25Z
user: login: JakaNin
state: APPROVED
author_association: NONE
submitted_at: 2025-05-18T23:34:59Z

Review comments:
diff_hunk: @@ -180,6 +181,120 @@ export class McpHub {
 		}
 	}
 
path: src/services/mcp/McpHub.ts
user: login: celestial-vault
body: Add new function that doesn't send messages to webview.
created_at: 2025-05-18T05:46:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -494,6 +609,36 @@ export class McpHub {
 		this.fileWatchers.clear()
 	}
 
path: src/services/mcp/McpHub.ts
user: login: celestial-vault
body: Add new function that doesn't send messages to webview and returns servers
created_at: 2025-05-18T05:46:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -46,6 +46,7 @@ const ServerRow = ({
 
 	const [isExpanded, setIsExpanded] = useState(false)
 	const [isDeleting, setIsDeleting] = useState(false)
path: webview-ui/src/components/mcp/configuration/tabs/installed/server-row/ServerRow.tsx
user: login: celestial-vault
body: Handling the connecting state as this was being done by the backend before. (crazy coupling)
created_at: 2025-05-18T05:47:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,6 +32,11 @@ message AddRemoteMcpServerRequest {
   string server_url = 3;
 }
 
+message RestartMcpServerRequest {
+  Metadata metadata = 1;
+  string server_name = 2;
+}
path: proto/mcp.proto
user: login: canvrno
body: The `StringRequest` message in `common.proto` can be used here instead of creating a bespoke message.
created_at: 2025-05-18T06:04:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,6 +32,11 @@ message AddRemoteMcpServerRequest {
   string server_url = 3;
 }
 
+message RestartMcpServerRequest {
+  Metadata metadata = 1;
+  string server_name = 2;
+}
path: proto/mcp.proto
user: login: celestial-vault
body: Is that necessary? I saw that but prefer just making it explicit.

If that's our standard then I can switch it

@canvrno 
created_at: 2025-05-18T17:27:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,6 +32,11 @@ message AddRemoteMcpServerRequest {
   string server_url = 3;
 }
 
+message RestartMcpServerRequest {
+  Metadata metadata = 1;
+  string server_name = 2;
+}
path: proto/mcp.proto
user: login: canvrno
body: Yeah, where possible we try to re-use the common messages (or sometimes modify existing ones) to keep things neat. Since this one just requires a single string (and metadata), it is a good candidate for dedup.
created_at: 2025-05-18T17:35:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,6 +32,11 @@ message AddRemoteMcpServerRequest {
   string server_url = 3;
 }
 
+message RestartMcpServerRequest {
+  Metadata metadata = 1;
+  string server_name = 2;
+}
path: proto/mcp.proto
user: login: celestial-vault
body: Alright
created_at: 2025-05-18T17:57:53Z
author_association: CONTRIBUTOR

PR comments:
user: login: changeset-bot[bot]
created_at: 2025-05-18T05:43:44Z
body: ###  🦋  Changeset detected

Latest commit: d2fa53cd892ce84f5add02a43c0089126f80a62a

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name       | Type  |
| ---------- | ----- |
| claude-dev | Minor |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/cline/cline/new/migrate-restartMcpServer-protobus?filename=.changeset/tough-numbers-poke.md&value=---%0A%22claude-dev%22%3A%20patch%0A---%0A%0AMigrate%20restartMcpServer%20protobus%0A)


author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-18T05:47:31Z
body: <!-- COVERAGE_REPORT -->
## Coverage Report

### Extension Coverage

Base branch: 45%

PR branch: 45%

✅ Coverage increased or remained the same

### Webview Coverage

Base branch: 12%

PR branch: 12%

⚠️ **Warning: Coverage decreased by 0.01%**

Consider adding tests to cover your changes.

### Overall Assessment

⚠️ **Test coverage has decreased in this PR**

Please consider adding tests to maintain or improve coverage.



<sub>Last updated: 2025-05-18T21:56:10.254434</sub>
author_association: CONTRIBUTOR