@@ -6,6 +6,7 @@ import (
6 "encoding/json"
6 "encoding/json"
@@ -110,9 +111,10 @@ func runTool(ctx context.Context, name, toolName string, input string) (tools.To
110 if err := json.Unmarshal([]byte(input), &args); err != nil {
111 if err := json.Unmarshal([]byte(input), &args); err != nil {
111 return tools.NewTextErrorResponse(fmt.Sprintf("error parsing parameters: %s", err)), nil
112 return tools.NewTextErrorResponse(fmt.Sprintf("error parsing parameters: %s", err)), nil
113− c, ok := mcpClients.Get(name)
114+
114− if !ok {
115+ c, err := getOrRenewClient(ctx, name)
115− return tools.NewTextErrorResponse("mcp '" + name + "' not available"), nil
117+ return tools.NewTextErrorResponse(err.Error()), nil
117 result, err := c.CallTool(ctx, mcp.CallToolRequest{
119 result, err := c.CallTool(ctx, mcp.CallToolRequest{
118 Params: mcp.CallToolParams{
120 Params: mcp.CallToolParams{
@@ -135,6 +137,33 @@ func runTool(ctx context.Context, name, toolName string, input string) (tools.To
135 return tools.NewTextResponse(strings.Join(output, "\n")), nil
137 return tools.NewTextResponse(strings.Join(output, "\n")), nil
140+func getOrRenewClient(ctx context.Context, name string) (*client.Client, error) {
141+ c, ok := mcpClients.Get(name)
143+ return nil, fmt.Errorf("mcp '%s' not available", name)
146+ m := config.Get().MCP[name]
147+ state, _ := mcpStates.Get(name)
149+ pingCtx, cancel := context.WithTimeout(ctx, mcpTimeout(m))
151+ err := c.Ping(pingCtx)
155+ updateMCPState(name, MCPStateError, err, nil, state.ToolCount)
157+ c, err = createAndInitializeClient(ctx, name, m)
162+ updateMCPState(name, MCPStateConnected, nil, c, state.ToolCount)
163+ mcpClients.Set(name, c)
138func (b *McpTool) Run(ctx context.Context, params tools.ToolCall) (tools.ToolResponse, error) {
167func (b *McpTool) Run(ctx context.Context, params tools.ToolCall) (tools.ToolResponse, error) {
139 sessionID, messageID := tools.GetContextValues(ctx)
168 sessionID, messageID := tools.GetContextValues(ctx)
140 if sessionID == "" || messageID == "" {
169 if sessionID == "" || messageID == "" {
@@ -187,11 +216,7 @@ func SubscribeMCPEvents(ctx context.Context) <-chan pubsub.Event[MCPEvent] {
188// GetMCPStates returns the current state of all MCP clients
217// GetMCPStates returns the current state of all MCP clients
189func GetMCPStates() map[string]MCPClientInfo {
218func GetMCPStates() map[string]MCPClientInfo {
190− states := make(map[string]MCPClientInfo)
191− for name, info := range mcpStates.Seq2() {
219+ return maps.Collect(mcpStates.Seq2())
197// GetMCPState returns the state of a specific MCP client
222// GetMCPState returns the state of a specific MCP client
@@ -275,32 +300,12 @@ func doGetMCPTools(ctx context.Context, permissions permission.Service, cfg *con
278− timeout := time.Duration(cmp.Or(m.Timeout, 15)) * time.Second
279− ctx, cancel := context.WithTimeout(ctx, timeout)
303+ ctx, cancel := context.WithTimeout(ctx, mcpTimeout(m))
280 defer cancel()
304 defer cancel()
281− c, err := createMcpClient(m)
305+ c, err := createAndInitializeClient(ctx, name, m)
282 if err != nil {
306 if err != nil {
283− updateMCPState(name, MCPStateError, err, nil, 0)
284− slog.Error("error creating mcp client", "error", err, "name", name)
287− // Only call Start() for non-stdio clients, as stdio clients auto-start
288− if m.Type != config.MCPStdio {
289− if err := c.Start(ctx); err != nil {
290− updateMCPState(name, MCPStateError, err, nil, 0)
291− slog.Error("error starting mcp client", "error", err, "name", name)
296− if _, err := c.Initialize(ctx, mcpInitRequest); err != nil {
297− updateMCPState(name, MCPStateError, err, nil, 0)
298− slog.Error("error initializing mcp client", "error", err, "name", name)
303− slog.Info("Initialized mcp client", "name", name)
304 mcpClients.Set(name, c)
309 mcpClients.Set(name, c)
306 tools := getTools(ctx, name, permissions, c, cfg.WorkingDir())
311 tools := getTools(ctx, name, permissions, c, cfg.WorkingDir())
@@ -312,6 +317,33 @@ func doGetMCPTools(ctx context.Context, permissions permission.Service, cfg *con
312 return slices.Collect(result.Seq())
317 return slices.Collect(result.Seq())
320+func createAndInitializeClient(ctx context.Context, name string, m config.MCPConfig) (*client.Client, error) {
321+ c, err := createMcpClient(m)
323+ updateMCPState(name, MCPStateError, err, nil, 0)
324+ slog.Error("error creating mcp client", "error", err, "name", name)
327+ // Only call Start() for non-stdio clients, as stdio clients auto-start
328+ if m.Type != config.MCPStdio {
329+ if err := c.Start(ctx); err != nil {
330+ updateMCPState(name, MCPStateError, err, nil, 0)
331+ slog.Error("error starting mcp client", "error", err, "name", name)
336+ if _, err := c.Initialize(ctx, mcpInitRequest); err != nil {
337+ updateMCPState(name, MCPStateError, err, nil, 0)
338+ slog.Error("error initializing mcp client", "error", err, "name", name)
343+ slog.Info("Initialized mcp client", "name", name)
315func createMcpClient(m config.MCPConfig) (*client.Client, error) {
347func createMcpClient(m config.MCPConfig) (*client.Client, error) {
316 switch m.Type {
348 switch m.Type {
317 case config.MCPStdio:
349 case config.MCPStdio:
@@ -343,3 +375,7 @@ type mcpLogger struct{}
344func (l mcpLogger) Errorf(format string, v ...any) { slog.Error(fmt.Sprintf(format, v...)) }
376func (l mcpLogger) Errorf(format string, v ...any) { slog.Error(fmt.Sprintf(format, v...)) }
345func (l mcpLogger) Infof(format string, v ...any) { slog.Info(fmt.Sprintf(format, v...)) }
377func (l mcpLogger) Infof(format string, v ...any) { slog.Info(fmt.Sprintf(format, v...)) }
379+func mcpTimeout(m config.MCPConfig) time.Duration {
380+ return time.Duration(cmp.Or(m.Timeout, 15)) * time.Second