mirror of
https://github.com/cline/cline.git
synced 2026-09-06 20:41:02 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d44c9605c | |||
| 60cb284f3d |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Move updateMcpTimeout message to protobus
|
||||
+13
-6
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
+11
-3
@@ -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 = () => {
|
||||
|
||||
Reference in New Issue
Block a user