@@ -16,6 +16,7 @@ import * as vscode from "vscode"
1616import { z } from "zod"
1717import { ClineProvider , GlobalFileNames } from "../../core/webview/ClineProvider"
1818import {
19+ DEFAULT_MCP_TIMEOUT_SECONDS ,
1920 McpMode ,
2021 McpResource ,
2122 McpResourceResponse ,
@@ -26,7 +27,7 @@ import {
2627} from "../../shared/mcp"
2728import { fileExistsAtPath } from "../../utils/fs"
2829import { arePathsEqual } from "../../utils/path"
29-
30+ import { secondsToMs } from "../../utils/time"
3031export type McpConnection = {
3132 server : McpServer
3233 client : Client
@@ -42,6 +43,7 @@ const StdioConfigSchema = z.object({
4243 env : z . record ( z . string ( ) ) . optional ( ) ,
4344 autoApprove : AutoApproveSchema . optional ( ) ,
4445 disabled : z . boolean ( ) . optional ( ) ,
46+ timeout : z . number ( ) . min ( 1 ) . max ( 3600 ) . optional ( ) . default ( DEFAULT_MCP_TIMEOUT_SECONDS ) ,
4547} )
4648
4749const McpSettingsSchema = z . object ( {
@@ -242,28 +244,6 @@ export class McpHub {
242244 }
243245 transport . start = async ( ) => { } // No-op now, .connect() won't fail
244246
245- // // Set up notification handlers
246- // client.setNotificationHandler(
247- // // @ts -ignore-next-line
248- // { method: "notifications/tools/list_changed" },
249- // async () => {
250- // console.log(`Tools changed for server: ${name}`)
251- // connection.server.tools = await this.fetchTools(name)
252- // await this.notifyWebviewOfServerChanges()
253- // },
254- // )
255-
256- // client.setNotificationHandler(
257- // // @ts -ignore-next-line
258- // { method: "notifications/resources/list_changed" },
259- // async () => {
260- // console.log(`Resources changed for server: ${name}`)
261- // connection.server.resources = await this.fetchResources(name)
262- // connection.server.resourceTemplates = await this.fetchResourceTemplates(name)
263- // await this.notifyWebviewOfServerChanges()
264- // },
265- // )
266-
267247 // Connect
268248 await client . connect ( transport )
269249 connection . server . status = "connected"
@@ -343,10 +323,6 @@ export class McpHub {
343323 const connection = this . connections . find ( ( conn ) => conn . server . name === name )
344324 if ( connection ) {
345325 try {
346- // connection.client.removeNotificationHandler("notifications/tools/list_changed")
347- // connection.client.removeNotificationHandler("notifications/resources/list_changed")
348- // connection.client.removeNotificationHandler("notifications/stderr")
349- // connection.client.removeNotificationHandler("notifications/stderr")
350326 await connection . transport . close ( )
351327 await connection . client . close ( )
352328 } catch ( error ) {
@@ -563,6 +539,7 @@ export class McpHub {
563539 if ( connection . server . disabled ) {
564540 throw new Error ( `Server "${ serverName } " is disabled` )
565541 }
542+
566543 return await connection . client . request (
567544 {
568545 method : "resources/read" ,
@@ -586,6 +563,17 @@ export class McpHub {
586563 throw new Error ( `Server "${ serverName } " is disabled and cannot be used` )
587564 }
588565
566+ let timeout = secondsToMs ( DEFAULT_MCP_TIMEOUT_SECONDS ) // sdk expects ms
567+
568+ try {
569+ const config = JSON . parse ( connection . server . config )
570+ const parsedConfig = StdioConfigSchema . parse ( config )
571+ timeout = secondsToMs ( parsedConfig . timeout )
572+ } catch ( error ) {
573+ console . error ( `Failed to parse timeout configuration for server ${ serverName } : ${ error } ` )
574+ // Continue with default timeout
575+ }
576+
589577 return await connection . client . request (
590578 {
591579 method : "tools/call" ,
@@ -595,6 +583,9 @@ export class McpHub {
595583 } ,
596584 } ,
597585 CallToolResultSchema ,
586+ {
587+ timeout,
588+ } ,
598589 )
599590 }
600591
@@ -663,6 +654,47 @@ export class McpHub {
663654 }
664655 }
665656
657+ public async updateServerTimeout ( serverName : string , timeout : number ) : Promise < void > {
658+ try {
659+ // Validate timeout against schema
660+ const setConfigResult = StdioConfigSchema . shape . timeout . safeParse ( timeout )
661+ if ( ! setConfigResult . success ) {
662+ throw new Error ( `Invalid timeout value: ${ timeout } . Must be between 1 and 3600 seconds.` )
663+ }
664+
665+ const settingsPath = await this . getMcpSettingsFilePath ( )
666+ const content = await fs . readFile ( settingsPath , "utf-8" )
667+ const config = JSON . parse ( content )
668+
669+ if ( ! config . mcpServers ?. [ serverName ] ) {
670+ throw new Error ( `Server "${ serverName } " not found in settings` )
671+ }
672+
673+ // Update the timeout in the config
674+ config . mcpServers [ serverName ] = {
675+ ...config . mcpServers [ serverName ] ,
676+ timeout,
677+ }
678+
679+ // Write updated config back to file
680+ await fs . writeFile ( settingsPath , JSON . stringify ( config , null , 2 ) )
681+
682+ // Update server connections to apply the new timeout
683+ await this . updateServerConnections ( config . mcpServers )
684+
685+ vscode . window . showInformationMessage ( `Updated timeout to ${ timeout } seconds` )
686+ } catch ( error ) {
687+ console . error ( "Failed to update server timeout:" , error )
688+ if ( error instanceof Error ) {
689+ console . error ( "Error details:" , error . message , error . stack )
690+ }
691+ vscode . window . showErrorMessage (
692+ `Failed to update server timeout: ${ error instanceof Error ? error . message : String ( error ) } ` ,
693+ )
694+ throw error
695+ }
696+ }
697+
666698 async dispose ( ) : Promise < void > {
667699 this . removeAllFileWatchers ( )
668700 for ( const connection of this . connections ) {
0 commit comments