Compare commits

...

2 Commits

Author SHA1 Message Date
celestial-vault 0d44c9605c changeset 2025-04-23 15:48:45 -07:00
celestial-vault 60cb284f3d updateMcpTimeout protobus conversion 2025-04-23 15:48:18 -07:00
9 changed files with 325 additions and 124 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": minor
---
Move updateMcpTimeout message to protobus
+13 -6
View File
@@ -6,6 +6,19 @@ import "common.proto";
service McpService {
rpc toggleMcpServer(ToggleMcpServerRequest) returns (McpServers);
rpc updateMcpTimeout(UpdateMcpTimeoutRequest) returns (McpServers);
}
message ToggleMcpServerRequest {
Metadata metadata = 1;
string server_name = 2;
bool disabled = 3;
}
message UpdateMcpTimeoutRequest {
Metadata metadata = 1;
string server_name = 2;
int32 timeout = 3;
}
message McpTool {
@@ -49,12 +62,6 @@ message McpServer {
optional int32 timeout = 9;
}
message ToggleMcpServerRequest {
Metadata metadata = 1;
string server_name = 2;
bool disabled = 3;
}
message McpServers {
repeated McpServer mcp_servers = 1;
}
-10
View File
@@ -682,16 +682,6 @@ export class Controller {
}
break
}
case "updateMcpTimeout": {
try {
if (message.serverName && message.timeout) {
await this.mcpHub?.updateServerTimeout(message.serverName, message.timeout)
}
} catch (error) {
console.error(`Failed to update timeout for server ${message.serverName}:`, error)
}
break
}
case "openExtensionSettings": {
const settingsFilter = message.text || ""
await vscode.commands.executeCommand(
+2
View File
@@ -4,9 +4,11 @@
// Import all method implementations
import { registerMethod } from "./index"
import { toggleMcpServer } from "./toggleMcpServer"
import { updateMcpTimeout } from "./updateMcpTimeout"
// Register all mcp service methods
export function registerAllMethods(): void {
// Register each method with the registry
registerMethod("toggleMcpServer", toggleMcpServer)
registerMethod("updateMcpTimeout", updateMcpTimeout)
}
@@ -0,0 +1,27 @@
import { convertMcpServersToProtoMcpServers } from "@/shared/proto-conversions/mcp/mcp-server-conversion"
import { Controller } from ".."
import { UpdateMcpTimeoutRequest, McpServers } from "../../../shared/proto/mcp"
/**
* Updates the timeout configuration for an MCP server.
* @param controller - The Controller instance
* @param request - Contains server name and timeout value
* @returns Array of updated McpServer objects
*/
export async function updateMcpTimeout(controller: Controller, request: UpdateMcpTimeoutRequest): Promise<McpServers> {
try {
if (request.serverName && typeof request.serverName === "string" && typeof request.timeout === "number") {
const mcpServers = await controller.mcpHub?.updateServerTimeoutRPC(request.serverName, request.timeout)
console.log("mcpServers", mcpServers)
const convertedMcpServers = convertMcpServersToProtoMcpServers(mcpServers)
console.log("convertedMcpServers", convertedMcpServers)
return { mcpServers: convertedMcpServers }
} else {
console.error("Server name and timeout are required")
throw new Error("Server name and timeout are required")
}
} catch (error) {
console.error(`Failed to update timeout for server ${request.serverName}:`, error)
throw error
}
}
+61 -4
View File
@@ -329,10 +329,9 @@ export class McpHub {
autoApprove: autoApproveConfig.includes(tool.name),
}))
// console.log(`[MCP] Fetched tools for ${serverName}:`, tools)
return tools
} catch (error) {
// console.error(`Failed to fetch tools for ${serverName}:`, error)
console.error(`Failed to fetch tools for ${serverName}:`, error)
return []
}
}
@@ -377,6 +376,53 @@ export class McpHub {
}
}
async updateServerConnectionsRPC(newServers: Record<string, McpServerConfig>): Promise<void> {
this.isConnecting = true
this.removeAllFileWatchers()
const currentNames = new Set(this.connections.map((conn) => conn.server.name))
const newNames = new Set(Object.keys(newServers))
// Delete removed servers
for (const name of currentNames) {
if (!newNames.has(name)) {
await this.deleteConnection(name)
console.log(`Deleted MCP server: ${name}`)
}
}
// Update or add servers
for (const [name, config] of Object.entries(newServers)) {
const currentConnection = this.connections.find((conn) => conn.server.name === name)
if (!currentConnection) {
// New server
try {
if (config.transportType === "stdio") {
this.setupFileWatcher(name, config)
}
await this.connectToServer(name, config)
} catch (error) {
console.error(`Failed to connect to new MCP server ${name}:`, error)
}
} else if (!deepEqual(JSON.parse(currentConnection.server.config), config)) {
// Existing server with changed config
try {
if (config.transportType === "stdio") {
this.setupFileWatcher(name, config)
}
await this.deleteConnection(name)
await this.connectToServer(name, config)
console.log(`Reconnected MCP server with updated config: ${name}`)
} catch (error) {
console.error(`Failed to reconnect MCP server ${name}:`, error)
}
}
// If server exists with same config, do nothing
}
this.isConnecting = false
}
async updateServerConnections(newServers: Record<string, McpServerConfig>): Promise<void> {
this.isConnecting = true
this.removeAllFileWatchers()
@@ -719,7 +765,7 @@ export class McpHub {
}
}
public async updateServerTimeout(serverName: string, timeout: number): Promise<void> {
public async updateServerTimeoutRPC(serverName: string, timeout: number): Promise<McpServer[]> {
try {
// Validate timeout against schema
const setConfigResult = BaseConfigSchema.shape.timeout.safeParse(timeout)
@@ -742,7 +788,18 @@ export class McpHub {
await fs.writeFile(settingsPath, JSON.stringify(config, null, 2))
await this.updateServerConnections(config.mcpServers)
await this.updateServerConnectionsRPC(config.mcpServers)
const serverOrder = Object.keys(config.mcpServers || {})
const updatedMcpServers = [...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)
return updatedMcpServers
} catch (error) {
console.error("Failed to update server timeout:", error)
if (error instanceof Error) {
-2
View File
@@ -47,7 +47,6 @@ export interface WebviewMessage {
| "openExtensionSettings"
| "requestVsCodeLmModels"
| "toggleToolAutoApprove"
| "toggleMcpServer"
| "getLatestState"
| "accountLoginClicked"
| "accountLogoutClicked"
@@ -62,7 +61,6 @@ export interface WebviewMessage {
| "fetchLatestMcpServersFromHub"
| "telemetrySetting"
| "openSettings"
| "updateMcpTimeout"
| "fetchOpenGraphData"
| "checkIsImageUrl"
| "invoke"
+206 -99
View File
@@ -53,6 +53,18 @@ export function mcpServerStatusToJSON(object: McpServerStatus): string {
}
}
export interface ToggleMcpServerRequest {
metadata?: Metadata | undefined
serverName: string
disabled: boolean
}
export interface UpdateMcpTimeoutRequest {
metadata?: Metadata | undefined
serverName: string
timeout: number
}
export interface McpTool {
name: string
description?: string | undefined
@@ -86,16 +98,196 @@ export interface McpServer {
timeout?: number | undefined
}
export interface ToggleMcpServerRequest {
metadata?: Metadata | undefined
serverName: string
disabled: boolean
}
export interface McpServers {
mcpServers: McpServer[]
}
function createBaseToggleMcpServerRequest(): ToggleMcpServerRequest {
return { metadata: undefined, serverName: "", disabled: false }
}
export const ToggleMcpServerRequest: MessageFns<ToggleMcpServerRequest> = {
encode(message: ToggleMcpServerRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.metadata !== undefined) {
Metadata.encode(message.metadata, writer.uint32(10).fork()).join()
}
if (message.serverName !== "") {
writer.uint32(18).string(message.serverName)
}
if (message.disabled !== false) {
writer.uint32(24).bool(message.disabled)
}
return writer
},
decode(input: BinaryReader | Uint8Array, length?: number): ToggleMcpServerRequest {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input)
let end = length === undefined ? reader.len : reader.pos + length
const message = createBaseToggleMcpServerRequest()
while (reader.pos < end) {
const tag = reader.uint32()
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break
}
message.metadata = Metadata.decode(reader, reader.uint32())
continue
}
case 2: {
if (tag !== 18) {
break
}
message.serverName = reader.string()
continue
}
case 3: {
if (tag !== 24) {
break
}
message.disabled = reader.bool()
continue
}
}
if ((tag & 7) === 4 || tag === 0) {
break
}
reader.skip(tag & 7)
}
return message
},
fromJSON(object: any): ToggleMcpServerRequest {
return {
metadata: isSet(object.metadata) ? Metadata.fromJSON(object.metadata) : undefined,
serverName: isSet(object.serverName) ? globalThis.String(object.serverName) : "",
disabled: isSet(object.disabled) ? globalThis.Boolean(object.disabled) : false,
}
},
toJSON(message: ToggleMcpServerRequest): unknown {
const obj: any = {}
if (message.metadata !== undefined) {
obj.metadata = Metadata.toJSON(message.metadata)
}
if (message.serverName !== "") {
obj.serverName = message.serverName
}
if (message.disabled !== false) {
obj.disabled = message.disabled
}
return obj
},
create<I extends Exact<DeepPartial<ToggleMcpServerRequest>, I>>(base?: I): ToggleMcpServerRequest {
return ToggleMcpServerRequest.fromPartial(base ?? ({} as any))
},
fromPartial<I extends Exact<DeepPartial<ToggleMcpServerRequest>, I>>(object: I): ToggleMcpServerRequest {
const message = createBaseToggleMcpServerRequest()
message.metadata =
object.metadata !== undefined && object.metadata !== null ? Metadata.fromPartial(object.metadata) : undefined
message.serverName = object.serverName ?? ""
message.disabled = object.disabled ?? false
return message
},
}
function createBaseUpdateMcpTimeoutRequest(): UpdateMcpTimeoutRequest {
return { metadata: undefined, serverName: "", timeout: 0 }
}
export const UpdateMcpTimeoutRequest: MessageFns<UpdateMcpTimeoutRequest> = {
encode(message: UpdateMcpTimeoutRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.metadata !== undefined) {
Metadata.encode(message.metadata, writer.uint32(10).fork()).join()
}
if (message.serverName !== "") {
writer.uint32(18).string(message.serverName)
}
if (message.timeout !== 0) {
writer.uint32(24).int32(message.timeout)
}
return writer
},
decode(input: BinaryReader | Uint8Array, length?: number): UpdateMcpTimeoutRequest {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input)
let end = length === undefined ? reader.len : reader.pos + length
const message = createBaseUpdateMcpTimeoutRequest()
while (reader.pos < end) {
const tag = reader.uint32()
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break
}
message.metadata = Metadata.decode(reader, reader.uint32())
continue
}
case 2: {
if (tag !== 18) {
break
}
message.serverName = reader.string()
continue
}
case 3: {
if (tag !== 24) {
break
}
message.timeout = reader.int32()
continue
}
}
if ((tag & 7) === 4 || tag === 0) {
break
}
reader.skip(tag & 7)
}
return message
},
fromJSON(object: any): UpdateMcpTimeoutRequest {
return {
metadata: isSet(object.metadata) ? Metadata.fromJSON(object.metadata) : undefined,
serverName: isSet(object.serverName) ? globalThis.String(object.serverName) : "",
timeout: isSet(object.timeout) ? globalThis.Number(object.timeout) : 0,
}
},
toJSON(message: UpdateMcpTimeoutRequest): unknown {
const obj: any = {}
if (message.metadata !== undefined) {
obj.metadata = Metadata.toJSON(message.metadata)
}
if (message.serverName !== "") {
obj.serverName = message.serverName
}
if (message.timeout !== 0) {
obj.timeout = Math.round(message.timeout)
}
return obj
},
create<I extends Exact<DeepPartial<UpdateMcpTimeoutRequest>, I>>(base?: I): UpdateMcpTimeoutRequest {
return UpdateMcpTimeoutRequest.fromPartial(base ?? ({} as any))
},
fromPartial<I extends Exact<DeepPartial<UpdateMcpTimeoutRequest>, I>>(object: I): UpdateMcpTimeoutRequest {
const message = createBaseUpdateMcpTimeoutRequest()
message.metadata =
object.metadata !== undefined && object.metadata !== null ? Metadata.fromPartial(object.metadata) : undefined
message.serverName = object.serverName ?? ""
message.timeout = object.timeout ?? 0
return message
},
}
function createBaseMcpTool(): McpTool {
return { name: "", description: undefined, inputSchema: undefined, autoApprove: undefined }
}
@@ -622,99 +814,6 @@ export const McpServer: MessageFns<McpServer> = {
},
}
function createBaseToggleMcpServerRequest(): ToggleMcpServerRequest {
return { metadata: undefined, serverName: "", disabled: false }
}
export const ToggleMcpServerRequest: MessageFns<ToggleMcpServerRequest> = {
encode(message: ToggleMcpServerRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.metadata !== undefined) {
Metadata.encode(message.metadata, writer.uint32(10).fork()).join()
}
if (message.serverName !== "") {
writer.uint32(18).string(message.serverName)
}
if (message.disabled !== false) {
writer.uint32(24).bool(message.disabled)
}
return writer
},
decode(input: BinaryReader | Uint8Array, length?: number): ToggleMcpServerRequest {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input)
let end = length === undefined ? reader.len : reader.pos + length
const message = createBaseToggleMcpServerRequest()
while (reader.pos < end) {
const tag = reader.uint32()
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break
}
message.metadata = Metadata.decode(reader, reader.uint32())
continue
}
case 2: {
if (tag !== 18) {
break
}
message.serverName = reader.string()
continue
}
case 3: {
if (tag !== 24) {
break
}
message.disabled = reader.bool()
continue
}
}
if ((tag & 7) === 4 || tag === 0) {
break
}
reader.skip(tag & 7)
}
return message
},
fromJSON(object: any): ToggleMcpServerRequest {
return {
metadata: isSet(object.metadata) ? Metadata.fromJSON(object.metadata) : undefined,
serverName: isSet(object.serverName) ? globalThis.String(object.serverName) : "",
disabled: isSet(object.disabled) ? globalThis.Boolean(object.disabled) : false,
}
},
toJSON(message: ToggleMcpServerRequest): unknown {
const obj: any = {}
if (message.metadata !== undefined) {
obj.metadata = Metadata.toJSON(message.metadata)
}
if (message.serverName !== "") {
obj.serverName = message.serverName
}
if (message.disabled !== false) {
obj.disabled = message.disabled
}
return obj
},
create<I extends Exact<DeepPartial<ToggleMcpServerRequest>, I>>(base?: I): ToggleMcpServerRequest {
return ToggleMcpServerRequest.fromPartial(base ?? ({} as any))
},
fromPartial<I extends Exact<DeepPartial<ToggleMcpServerRequest>, I>>(object: I): ToggleMcpServerRequest {
const message = createBaseToggleMcpServerRequest()
message.metadata =
object.metadata !== undefined && object.metadata !== null ? Metadata.fromPartial(object.metadata) : undefined
message.serverName = object.serverName ?? ""
message.disabled = object.disabled ?? false
return message
},
}
function createBaseMcpServers(): McpServers {
return { mcpServers: [] }
}
@@ -790,6 +889,14 @@ export const McpServiceDefinition = {
responseStream: false,
options: {},
},
updateMcpTimeout: {
name: "updateMcpTimeout",
requestType: UpdateMcpTimeoutRequest,
requestStream: false,
responseType: McpServers,
responseStream: false,
options: {},
},
},
} as const
@@ -18,6 +18,7 @@ import McpResourceRow from "./McpResourceRow"
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"
// constant JSX.Elements
const TimeoutOptions = [
{ value: "30", label: "30 seconds" },
@@ -77,11 +78,18 @@ const ServerRow = ({
const value = select.value
const num = parseInt(value)
setTimeoutValue(value)
vscode.postMessage({
type: "updateMcpTimeout",
McpServiceClient.updateMcpTimeout({
serverName: server.name,
timeout: num,
})
} as UpdateMcpTimeoutRequest)
.then((response: McpServers) => {
const mcpServers = convertProtoMcpServersToMcpServers(response.mcpServers)
setMcpServers(mcpServers)
})
.catch((error) => {
console.error("Error updating MCP server timeout", error)
})
}
const handleRestart = () => {