@@ -38,8 +38,22 @@ func parseLevel(level mcp.LoggingLevel) slog.Level {
3838 }
3939}
4040
41+// ClientSession wraps an mcp.ClientSession with a context cancel function so
42+// that the context created during session establishment is properly cleaned up
43+// on close.
44+type ClientSession struct {
45+ *mcp.ClientSession
46+ cancel context.CancelFunc
47+}
48+
49+// Close cancels the session context and then closes the underlying session.
50+func (s *ClientSession) Close() error {
51+ s.cancel()
52+ return s.ClientSession.Close()
53+}
54+
4155var (
42− sessions = csync.NewMap[string, *mcp.ClientSession]()
56+ sessions = csync.NewMap[string, *ClientSession]()
4357 states = csync.NewMap[string, ClientInfo]()
4458 broker = pubsub.NewBroker[Event]()
4559 initOnce sync.Once
@@ -102,7 +116,7 @@ type ClientInfo struct {
102116 Name string
103117 State State
104118 Error error
105− Client *mcp.ClientSession
119+ Client *ClientSession
106120 Counts Counts
107121 ConnectedAt time.Time
108122}
@@ -239,7 +253,7 @@ func WaitForInit(ctx context.Context) error {
239253 }
240254}
241255
242−func getOrRenewClient(ctx context.Context, cfg *config.Config, name string) (*mcp.ClientSession, error) {
256+func getOrRenewClient(ctx context.Context, cfg *config.Config, name string) (*ClientSession, error) {
243257 sess, ok := sessions.Get(name)
244258 if !ok {
245259 return nil, fmt.Errorf("mcp '%s' not available", name)
@@ -268,7 +282,7 @@ func getOrRenewClient(ctx context.Context, cfg *config.Config, name string) (*mc
268282}
269283
270284// updateState updates the state of an MCP client and publishes an event
271−func updateState(name string, state State, err error, client *mcp.ClientSession, counts Counts) {
285+func updateState(name string, state State, err error, client *ClientSession, counts Counts) {
272286 info := ClientInfo{
273287 Name: name,
274288 State: state,
@@ -294,7 +308,7 @@ func updateState(name string, state State, err error, client *mcp.ClientSession,
294308 })
295309}
296310
297−func createSession(ctx context.Context, name string, m config.MCPConfig, resolver config.VariableResolver) (*mcp.ClientSession, error) {
311+func createSession(ctx context.Context, name string, m config.MCPConfig, resolver config.VariableResolver) (*ClientSession, error) {
298312 timeout := mcpTimeout(m)
299313 mcpCtx, cancel := context.WithCancel(ctx)
300314 cancelTimer := time.AfterFunc(timeout, cancel)
@@ -352,7 +366,7 @@ func createSession(ctx context.Context, name string, m config.MCPConfig, resolve
352366
353367 cancelTimer.Stop()
354368 slog.Debug("MCP client initialized", "name", name)
355− return session, nil
369+ return &ClientSession{session, cancel}, nil
356370}
357371
358372// maybeStdioErr if a stdio mcp prints an error in non-json format, it'll fail