Skip to content

Commit a8ac150

Browse files
philzsketch
andcommitted
sketch/mcp: fix mcp sse stream context cancellation
The SSE stream was being started with a timeout context that got canceled immediately after connection establishment, causing 'context canceled' errors and breaking MCP tool execution with 'Could not find session' errors. Separate connection establishment timeout from long-running SSE stream context: - Use agent's main context for mcpClient.Start() (SSE stream lifecycle) - Use separate timeout context for Initialize() and ListTools() (connection only) Fixes SSE stream persistence and enables successful MCP tool execution. Co-Authored-By: sketch <hello@sketch.dev> Change-ID: sb595ea17e6f1205ck
1 parent 693c950 commit a8ac150

File tree

1 file changed

+17
-13
lines changed

1 file changed

+17
-13
lines changed

mcp/client.go

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,15 @@ func (m *MCPManager) ConnectToServerConfigs(ctx context.Context, serverConfigs [
106106
}
107107

108108
results := make(chan result, len(serverConfigs))
109-
ctxWithTimeout, cancel := context.WithTimeout(ctx, timeout)
110-
defer cancel()
109+
// Create a timeout context only for the connection establishment goroutines
110+
connectionCtx, connectionCancel := context.WithTimeout(context.Background(), timeout)
111+
defer connectionCancel()
111112

112113
for _, config := range serverConfigs {
113114
go func(cfg ServerConfig) {
114115
slog.InfoContext(ctx, "Connecting to MCP server", "server", cfg.Name, "type", cfg.Type, "url", cfg.URL, "command", cfg.Command)
115-
tools, originalToolNames, err := m.connectToServerWithNames(ctxWithTimeout, cfg)
116+
// Pass both the long-running context (ctx) and the connection timeout context
117+
tools, originalToolNames, err := m.connectToServerWithNames(ctx, connectionCtx, cfg)
116118
results <- result{
117119
tools: tools,
118120
err: err,
@@ -143,7 +145,7 @@ NextServer:
143145
connections = append(connections, connection)
144146
slog.InfoContext(ctx, "Successfully connected to MCP server", "server", res.serverName, "tools", len(res.tools), "tool_names", res.originalTools)
145147
}
146-
case <-ctxWithTimeout.Done():
148+
case <-connectionCtx.Done():
147149
errors = append(errors, fmt.Errorf("timeout connecting to MCP servers"))
148150
break NextServer
149151
}
@@ -153,8 +155,8 @@ NextServer:
153155
}
154156

155157
// connectToServerWithNames connects to a single MCP server and returns tools with original names
156-
func (m *MCPManager) connectToServerWithNames(ctx context.Context, config ServerConfig) ([]*llm.Tool, []string, error) {
157-
tools, err := m.connectToServer(ctx, config)
158+
func (m *MCPManager) connectToServerWithNames(longRunningCtx context.Context, connectionCtx context.Context, config ServerConfig) ([]*llm.Tool, []string, error) {
159+
tools, err := m.connectToServer(longRunningCtx, connectionCtx, config)
158160
if err != nil {
159161
return nil, nil, err
160162
}
@@ -175,7 +177,9 @@ func (m *MCPManager) connectToServerWithNames(ctx context.Context, config Server
175177
}
176178

177179
// connectToServer connects to a single MCP server
178-
func (m *MCPManager) connectToServer(ctx context.Context, config ServerConfig) ([]*llm.Tool, error) {
180+
// longRunningCtx: context for the ongoing MCP client lifecycle (SSE streams)
181+
// connectionCtx: context with timeout for connection establishment only
182+
func (m *MCPManager) connectToServer(longRunningCtx context.Context, connectionCtx context.Context, config ServerConfig) ([]*llm.Tool, error) {
179183
var mcpClient *client.Client
180184
var err error
181185

@@ -220,12 +224,12 @@ func (m *MCPManager) connectToServer(ctx context.Context, config ServerConfig) (
220224
return nil, fmt.Errorf("failed to create MCP client: %w", err)
221225
}
222226

223-
// Start the client first
224-
if err := mcpClient.Start(ctx); err != nil {
227+
// Start the client with the long-running context for SSE streams
228+
if err := mcpClient.Start(longRunningCtx); err != nil {
225229
return nil, fmt.Errorf("failed to start MCP client: %w", err)
226230
}
227231

228-
// Initialize the client
232+
// Initialize the client with connection timeout context
229233
initReq := mcp.InitializeRequest{
230234
Params: mcp.InitializeParams{
231235
ProtocolVersion: mcp.LATEST_PROTOCOL_VERSION,
@@ -236,13 +240,13 @@ func (m *MCPManager) connectToServer(ctx context.Context, config ServerConfig) (
236240
},
237241
},
238242
}
239-
if _, err := mcpClient.Initialize(ctx, initReq); err != nil {
243+
if _, err := mcpClient.Initialize(connectionCtx, initReq); err != nil {
240244
return nil, fmt.Errorf("failed to initialize MCP client: %w", err)
241245
}
242246

243-
// Get available tools
247+
// Get available tools with connection timeout context
244248
toolsReq := mcp.ListToolsRequest{}
245-
toolsResp, err := mcpClient.ListTools(ctx, toolsReq)
249+
toolsResp, err := mcpClient.ListTools(connectionCtx, toolsReq)
246250
if err != nil {
247251
return nil, fmt.Errorf("failed to list tools: %w", err)
248252
}

0 commit comments

Comments
 (0)