@@ -5,6 +5,7 @@ import (
55 "encoding/json"
66 "fmt"
77 "log/slog"
8+ "sync"
89
910 "github.com/charmbracelet/crush/internal/config"
1011 "github.com/charmbracelet/crush/internal/llm/tools"
@@ -154,8 +155,6 @@ func NewMcpTool(name string, tool mcp.Tool, permissions permission.Service, mcpC
154155 }
155156}
156157
157−var mcpTools []tools.BaseTool
158−
159158func getTools(ctx context.Context, name string, m config.MCPConfig, permissions permission.Service, c MCPClient, workingDir string) []tools.BaseTool {
160159 var stdioTools []tools.BaseTool
161160 initRequest := mcp.InitializeRequest{}
@@ -183,50 +182,72 @@ func getTools(ctx context.Context, name string, m config.MCPConfig, permissions
183182 return stdioTools
184183}
185184
186−func GetMcpTools(ctx context.Context, permissions permission.Service, cfg *config.Config) []tools.BaseTool {
187− if len(mcpTools) > 0 {
188− return mcpTools
189− }
185+var (
186+ mcpToolsOnce sync.Once
187+ mcpTools []tools.BaseTool
188+)
189+
190+func GetMCPTools(ctx context.Context, permissions permission.Service, cfg *config.Config) []tools.BaseTool {
191+ mcpToolsOnce.Do(func() {
192+ mcpTools = doGetMCPTools(ctx, permissions, cfg)
193+ })
194+ return mcpTools
195+}
196+
197+func doGetMCPTools(ctx context.Context, permissions permission.Service, cfg *config.Config) []tools.BaseTool {
198+ var mu sync.Mutex
199+ var wg sync.WaitGroup
200+ var result []tools.BaseTool
190201 for name, m := range cfg.MCP {
191202 if m.Disabled {
192203 slog.Debug("skipping disabled mcp", "name", name)
193204 continue
194205 }
195− switch m.Type {
196− case config.MCPStdio:
197− c, err := client.NewStdioMCPClient(
198− m.Command,
199− m.Env,
200− m.Args...,
201− )
202− if err != nil {
203− slog.Error("error creating mcp client", "error", err)
204− continue
206+ wg.Add(1)
207+ go func(name string, m config.MCPConfig) {
208+ defer wg.Done()
209+ switch m.Type {
210+ case config.MCPStdio:
211+ c, err := client.NewStdioMCPClient(
212+ m.Command,
213+ m.Env,
214+ m.Args...,
215+ )
216+ if err != nil {
217+ slog.Error("error creating mcp client", "error", err)
218+ return
219+ }
220+
221+ mu.Lock()
222+ result = append(result, getTools(ctx, name, m, permissions, c, cfg.WorkingDir())...)
223+ mu.Unlock()
224+ case config.MCPHttp:
225+ c, err := client.NewStreamableHttpClient(
226+ m.URL,
227+ transport.WithHTTPHeaders(m.Headers),
228+ )
229+ if err != nil {
230+ slog.Error("error creating mcp client", "error", err)
231+ return
232+ }
233+ mu.Lock()
234+ result = append(result, getTools(ctx, name, m, permissions, c, cfg.WorkingDir())...)
235+ mu.Unlock()
236+ case config.MCPSse:
237+ c, err := client.NewSSEMCPClient(
238+ m.URL,
239+ client.WithHeaders(m.Headers),
240+ )
241+ if err != nil {
242+ slog.Error("error creating mcp client", "error", err)
243+ return
244+ }
245+ mu.Lock()
246+ result = append(result, getTools(ctx, name, m, permissions, c, cfg.WorkingDir())...)
247+ mu.Unlock()
205248 }
206−
207− mcpTools = append(mcpTools, getTools(ctx, name, m, permissions, c, cfg.WorkingDir())...)
208− case config.MCPHttp:
209− c, err := client.NewStreamableHttpClient(
210− m.URL,
211− transport.WithHTTPHeaders(m.Headers),
212− )
213− if err != nil {
214− slog.Error("error creating mcp client", "error", err)
215− continue
216− }
217− mcpTools = append(mcpTools, getTools(ctx, name, m, permissions, c, cfg.WorkingDir())...)
218− case config.MCPSse:
219− c, err := client.NewSSEMCPClient(
220− m.URL,
221− client.WithHeaders(m.Headers),
222− )
223− if err != nil {
224− slog.Error("error creating mcp client", "error", err)
225− continue
226− }
227− mcpTools = append(mcpTools, getTools(ctx, name, m, permissions, c, cfg.WorkingDir())...)
228− }
249+ }(name, m)
229250 }
230−
231− return mcpTools
251+ wg.Wait()
252+ return result
232253}