@@ -113,6 +113,23 @@ func NewRemoteToolset(name, urlString, transport string, headers map[string]stri
113113// because there is no live connection to monitor.
114114var errServerUnavailable = errors .New ("MCP server unavailable" )
115115
116+ const (
117+ // mcpInitTimeout is the maximum time allowed for the MCP initialization
118+ // handshake (connect + initialize). If a remote server accepts the TCP
119+ // connection but never responds, this prevents the agent from hanging
120+ // indefinitely.
121+ mcpInitTimeout = 2 * time .Minute
122+
123+ // mcpCallToolTimeout is the maximum time allowed for a single tool call.
124+ // Tool calls may be long-running (e.g. code execution), so this is
125+ // deliberately generous.
126+ mcpCallToolTimeout = 10 * time .Minute
127+
128+ // mcpListTimeout is the maximum time allowed for listing tools, prompts
129+ // or fetching a single prompt from the MCP server.
130+ mcpListTimeout = 1 * time .Minute
131+ )
132+
116133// Describe returns a short, user-visible description of this toolset instance.
117134// It never includes secrets.
118135func (ts * Toolset ) Describe () string {
@@ -176,6 +193,11 @@ func (ts *Toolset) doStart(ctx context.Context) error {
176193 // This is critical for OAuth flows where the toolset connection needs to remain alive after the initial HTTP request completes.
177194 ctx = context .WithoutCancel (ctx )
178195
196+ // Apply an initialization timeout so we don't hang forever if the
197+ // remote server accepts the connection but never responds.
198+ initCtx , cancel := context .WithTimeout (ctx , mcpInitTimeout )
199+ defer cancel ()
200+
179201 slog .Debug ("Starting MCP toolset" , "server" , ts .logID )
180202
181203 // Register notification handlers to invalidate caches when the server
@@ -219,7 +241,7 @@ func (ts *Toolset) doStart(ctx context.Context) error {
219241 const maxRetries = 3
220242 for attempt := 0 ; ; attempt ++ {
221243 var err error
222- result , err = ts .mcpClient .Initialize (ctx , initRequest )
244+ result , err = ts .mcpClient .Initialize (initCtx , initRequest )
223245 if err == nil {
224246 break
225247 }
@@ -247,8 +269,8 @@ func (ts *Toolset) doStart(ctx context.Context) error {
247269 slog .Debug ("MCP initialize failed to send initialized notification; retrying" , "id" , ts .logID , "attempt" , attempt + 1 , "backoff_ms" , backoff .Milliseconds ())
248270 select {
249271 case <- time .After (backoff ):
250- case <- ctx .Done ():
251- return fmt .Errorf ("failed to initialize MCP client: %w" , ctx .Err ())
272+ case <- initCtx .Done ():
273+ return fmt .Errorf ("failed to initialize MCP client: %w" , initCtx .Err ())
252274 }
253275 }
254276
0 commit comments