Skip to content

Commit 361b0cf

Browse files
celestial-vaultElephant Lumps
andauthored
migrate mcpServers (RooCodeInc#4010)
Co-authored-by: Elephant Lumps <[email protected]>
1 parent a952757 commit 361b0cf

File tree

6 files changed

+111
-9
lines changed

6 files changed

+111
-9
lines changed

proto/mcp.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ service McpService {
2020
// Subscribe to MCP marketplace catalog updates
2121
rpc subscribeToMcpMarketplaceCatalog(EmptyRequest) returns (stream McpMarketplaceCatalog);
2222
rpc getLatestMcpServers(Empty) returns (McpServers);
23+
24+
// Subscribe to MCP server updates
25+
rpc subscribeToMcpServers(EmptyRequest) returns (stream McpServers);
2326
}
2427

2528
message ToggleMcpServerRequest {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { Controller } from "../index"
2+
import { EmptyRequest } from "@shared/proto/common"
3+
import { McpServers } from "@shared/proto/mcp"
4+
import { StreamingResponseHandler, getRequestRegistry } from "../grpc-handler"
5+
import { convertMcpServersToProtoMcpServers } from "@shared/proto-conversions/mcp/mcp-server-conversion"
6+
7+
// Keep track of active subscriptions
8+
const activeMcpServersSubscriptions = new Set<StreamingResponseHandler>()
9+
10+
/**
11+
* Subscribe to MCP servers events
12+
* @param controller The controller instance
13+
* @param request The empty request
14+
* @param responseStream The streaming response handler
15+
* @param requestId The ID of the request (passed by the gRPC handler)
16+
*/
17+
export async function subscribeToMcpServers(
18+
controller: Controller,
19+
request: EmptyRequest,
20+
responseStream: StreamingResponseHandler,
21+
requestId?: string,
22+
): Promise<void> {
23+
// Add this subscription to the active subscriptions
24+
activeMcpServersSubscriptions.add(responseStream)
25+
26+
// Register cleanup when the connection is closed
27+
const cleanup = () => {
28+
activeMcpServersSubscriptions.delete(responseStream)
29+
}
30+
31+
// Register the cleanup function with the request registry if we have a requestId
32+
if (requestId) {
33+
getRequestRegistry().registerRequest(requestId, cleanup, { type: "mcpServers_subscription" }, responseStream)
34+
}
35+
36+
// Send initial state if available
37+
if (controller.mcpHub) {
38+
const mcpServers = controller.mcpHub.getServers()
39+
if (mcpServers.length > 0) {
40+
try {
41+
const protoServers = McpServers.create({
42+
mcpServers: convertMcpServersToProtoMcpServers(mcpServers),
43+
})
44+
await responseStream(
45+
protoServers,
46+
false, // Not the last message
47+
)
48+
} catch (error) {
49+
console.error("Error sending initial MCP servers:", error)
50+
activeMcpServersSubscriptions.delete(responseStream)
51+
}
52+
}
53+
}
54+
}
55+
56+
/**
57+
* Send an MCP servers update to all active subscribers
58+
* @param mcpServers The MCP servers to send
59+
*/
60+
export async function sendMcpServersUpdate(mcpServers: McpServers): Promise<void> {
61+
// Send the event to all active subscribers
62+
const promises = Array.from(activeMcpServersSubscriptions).map(async (responseStream) => {
63+
try {
64+
await responseStream(
65+
mcpServers,
66+
false, // Not the last message
67+
)
68+
} catch (error) {
69+
console.error("Error sending MCP servers update:", error)
70+
// Remove the subscription if there was an error
71+
activeMcpServersSubscriptions.delete(responseStream)
72+
}
73+
})
74+
75+
await Promise.all(promises)
76+
}

src/core/controller/mcp/updateMcpTimeout.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ export async function updateMcpTimeout(controller: Controller, request: UpdateMc
1212
try {
1313
if (request.serverName && typeof request.serverName === "string" && typeof request.timeout === "number") {
1414
const mcpServers = await controller.mcpHub?.updateServerTimeoutRPC(request.serverName, request.timeout)
15-
console.log("mcpServers", mcpServers)
1615
const convertedMcpServers = convertMcpServersToProtoMcpServers(mcpServers)
1716
console.log("convertedMcpServers", convertedMcpServers)
1817
return McpServers.create({ mcpServers: convertedMcpServers })

src/services/mcp/McpHub.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
ListToolsResultSchema,
1111
ReadResourceResultSchema,
1212
} from "@modelcontextprotocol/sdk/types.js"
13+
import { sendMcpServersUpdate } from "@core/controller/mcp/subscribeToMcpServers"
14+
import { convertMcpServersToProtoMcpServers } from "@shared/proto-conversions/mcp/mcp-server-conversion"
1315
import chokidar, { FSWatcher } from "chokidar"
1416
import { setTimeout as setTimeoutPromise } from "node:timers/promises"
1517
import deepEqual from "fast-deep-equal"
@@ -606,9 +608,13 @@ export class McpHub {
606608
const content = await fs.readFile(settingsPath, "utf-8")
607609
const config = JSON.parse(content)
608610
const serverOrder = Object.keys(config.mcpServers || {})
609-
await this.postMessageToWebview({
610-
type: "mcpServers",
611-
mcpServers: this.getSortedMcpServers(serverOrder),
611+
612+
// Get sorted servers
613+
const sortedServers = this.getSortedMcpServers(serverOrder)
614+
615+
// Send update using gRPC stream
616+
await sendMcpServersUpdate({
617+
mcpServers: convertMcpServersToProtoMcpServers(sortedServers),
612618
})
613619
}
614620

src/shared/ExtensionMessage.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ export interface ExtensionMessage {
1919
| "selectedImages"
2020
| "openAiModels"
2121
| "requestyModels"
22-
| "mcpServers"
2322
| "mcpDownloadDetails"
2423
| "userCreditsBalance"
2524
| "userCreditsUsage"

webview-ui/src/context/ExtensionStateContext.tsx

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
requestyDefaultModelInfo,
2727
} from "../../../src/shared/api"
2828
import { McpMarketplaceCatalog, McpServer, McpViewTab } from "../../../src/shared/mcp"
29+
import { convertProtoMcpServersToMcpServers } from "@shared/proto-conversions/mcp/mcp-server-conversion"
2930
import { convertTextMateToHljs } from "../utils/textMateToHljs"
3031
import { vscode } from "../utils/vscode"
3132
import { OpenRouterCompatibleModelInfo } from "@shared/proto/models"
@@ -223,10 +224,6 @@ export const ExtensionStateContextProvider: React.FC<{
223224
})
224225
break
225226
}
226-
case "mcpServers": {
227-
setMcpServers(message.mcpServers ?? [])
228-
break
229-
}
230227
}
231228
}, [])
232229

@@ -256,6 +253,7 @@ export const ExtensionStateContextProvider: React.FC<{
256253
relinquishControlCallbacks.current.delete(callback)
257254
}
258255
}, [])
256+
const mcpServersSubscriptionRef = useRef<(() => void) | null>(null)
259257

260258
// Subscribe to state updates and UI events using the gRPC streaming API
261259
useEffect(() => {
@@ -385,6 +383,22 @@ export const ExtensionStateContextProvider: React.FC<{
385383
onComplete: () => {},
386384
})
387385

386+
// Subscribe to MCP servers updates
387+
mcpServersSubscriptionRef.current = McpServiceClient.subscribeToMcpServers(EmptyRequest.create(), {
388+
onResponse: (response) => {
389+
console.log("[DEBUG] Received MCP servers update from gRPC stream")
390+
if (response.mcpServers) {
391+
setMcpServers(convertProtoMcpServersToMcpServers(response.mcpServers))
392+
}
393+
},
394+
onError: (error) => {
395+
console.error("Error in MCP servers subscription:", error)
396+
},
397+
onComplete: () => {
398+
console.log("MCP servers subscription completed")
399+
},
400+
})
401+
388402
// Subscribe to workspace file updates
389403
workspaceUpdatesUnsubscribeRef.current = FileServiceClient.subscribeToWorkspaceUpdates(EmptyRequest.create({}), {
390404
onResponse: (response) => {
@@ -592,6 +606,11 @@ export const ExtensionStateContextProvider: React.FC<{
592606
relinquishControlUnsubscribeRef.current()
593607
relinquishControlUnsubscribeRef.current = null
594608
}
609+
610+
if (mcpServersSubscriptionRef.current) {
611+
mcpServersSubscriptionRef.current()
612+
mcpServersSubscriptionRef.current = null
613+
}
595614
}
596615
}, [])
597616

0 commit comments

Comments
 (0)