fix(lsp): improve lsp tools (#2089)

Carlos Alexandro Becker created

With auto discovery, the user configured lsps might be empty, but we
might still configure some LSPs. We need to check the proper places, as
well as refresh the tool list if LSPs are actually started.

This is an alternative implementation to #2079

Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>

Change summary

internal/agent/coordinator.go    |  2 +-
internal/agent/tools/mcp/init.go |  1 +
internal/app/app.go              |  5 +----
internal/app/lsp.go              | 20 +++++++++++++++-----
4 files changed, 18 insertions(+), 10 deletions(-)

Detailed changes

internal/agent/coordinator.go 🔗

@@ -435,7 +435,7 @@ func (c *coordinator) buildTools(ctx context.Context, agent config.Agent) ([]fan
 		tools.NewWriteTool(c.lspClients, c.permissions, c.history, c.filetracker, c.cfg.WorkingDir()),
 	)
 
-	if len(c.cfg.LSP) > 0 {
+	if c.lspClients.Len() > 0 {
 		allTools = append(allTools, tools.NewDiagnosticsTool(c.lspClients), tools.NewReferencesTool(c.lspClients), tools.NewLSPRestartTool(c.lspClients))
 	}
 

internal/agent/tools/mcp/init.go 🔗

@@ -135,6 +135,7 @@ func Close() error {
 
 // Initialize initializes MCP clients based on the provided configuration.
 func Initialize(ctx context.Context, permissions permission.Service, cfg *config.Config) {
+	slog.Info("Initializing MCP clients")
 	var wg sync.WaitGroup
 	// Initialize states for all configured MCPs
 	for name, m := range cfg.MCP {

internal/app/app.go 🔗

@@ -109,10 +109,7 @@ func New(ctx context.Context, conn *sql.DB, cfg *config.Config) (*App, error) {
 	// Check for updates in the background.
 	go app.checkForUpdates(ctx)
 
-	go func() {
-		slog.Info("Initializing MCP clients")
-		mcp.Initialize(ctx, app.Permissions, cfg)
-	}()
+	go mcp.Initialize(ctx, app.Permissions, cfg)
 
 	// cleanup database upon app shutdown
 	app.cleanupFuncs = append(app.cleanupFuncs, conn.Close, mcp.Close)

internal/app/lsp.go 🔗

@@ -5,6 +5,7 @@ import (
 	"log/slog"
 	"os/exec"
 	"slices"
+	"sync"
 	"time"
 
 	"github.com/charmbracelet/crush/internal/config"
@@ -58,16 +59,25 @@ func (app *App) initLSPClients(ctx context.Context) {
 			updateLSPState(name, lsp.StateDisabled, nil, nil, 0)
 		}
 	}
+
+	var wg sync.WaitGroup
 	for name, server := range filtered {
 		if app.config.Options.AutoLSP != nil && !*app.config.Options.AutoLSP && !slices.Contains(userConfiguredLSPs, name) {
 			slog.Debug("Ignoring non user-define LSP client due to AutoLSP being disabled", "name", name)
 			continue
 		}
-		go app.createAndStartLSPClient(
-			ctx, name,
-			toOurConfig(server),
-			slices.Contains(userConfiguredLSPs, name),
-		)
+		wg.Go(func() {
+			app.createAndStartLSPClient(
+				ctx, name,
+				toOurConfig(server),
+				slices.Contains(userConfiguredLSPs, name),
+			)
+		})
+	}
+	wg.Wait()
+
+	if err := app.AgentCoordinator.UpdateModels(ctx); err != nil {
+		slog.Error("Failed to refresh tools after LSP startup", "error", err)
 	}
 }