diff --git a/internal/llm/agent/mcp-tools.go b/internal/llm/agent/mcp-tools.go index 19e95acc63448334364dd2b7819f541da44775c6..e263152bd6394d1d9fb923bdd69d669564152ec6 100644 --- a/internal/llm/agent/mcp-tools.go +++ b/internal/llm/agent/mcp-tools.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "log/slog" "maps" "net/http" @@ -257,7 +258,10 @@ func updateMCPState(name string, state MCPState, err error, client *mcp.ClientSe func CloseMCPClients() error { var errs []error for name, c := range mcpClients.Seq2() { - if err := c.Close(); err != nil { + if err := c.Close(); err != nil && + !errors.Is(err, io.EOF) && + !errors.Is(err, context.Canceled) && + err.Error() != "signal: killed" { errs = append(errs, fmt.Errorf("close mcp: %s: %w", name, err)) } } @@ -389,7 +393,6 @@ func createMCPSession(ctx context.Context, name string, m config.MCPConfig, reso if err != nil { updateMCPState(name, MCPStateError, maybeTimeoutErr(err, timeout), nil, MCPCounts{}) slog.Error("error starting mcp client", "error", err, "name", name) - _ = session.Close() cancel() return nil, err } diff --git a/internal/tui/components/lsp/lsp.go b/internal/tui/components/lsp/lsp.go index f5f4061045901c91ecb8bce1f47eab3ac1f7abcf..0c0384e91c36744b8f318f9bbc71e5e076a26abf 100644 --- a/internal/tui/components/lsp/lsp.go +++ b/internal/tui/components/lsp/lsp.go @@ -56,32 +56,7 @@ func RenderLSPList(lspClients *csync.Map[string, *lsp.Client], opts RenderOption break } - // Determine icon color and description based on state - icon := t.ItemOfflineIcon - description := l.LSP.Command - - if l.LSP.Disabled { - description = t.S().Subtle.Render("disabled") - } else if state, exists := lspStates[l.Name]; exists { - switch state.State { - case lsp.StateStarting: - icon = t.ItemBusyIcon - description = t.S().Subtle.Render("starting...") - case lsp.StateReady: - icon = t.ItemOnlineIcon - description = l.LSP.Command - case lsp.StateError: - icon = t.ItemErrorIcon - if state.Error != nil { - description = t.S().Subtle.Render(fmt.Sprintf("error: %s", state.Error.Error())) - } else { - description = t.S().Subtle.Render("error") - } - case lsp.StateDisabled: - icon = t.ItemOfflineIcon.Foreground(t.FgMuted) - description = t.S().Base.Foreground(t.FgMuted).Render("no root markers found") - } - } + icon, description := iconAndDescription(l, t, lspStates) // Calculate diagnostic counts if we have LSP clients var extraContent string @@ -134,6 +109,30 @@ func RenderLSPList(lspClients *csync.Map[string, *lsp.Client], opts RenderOption return lspList } +func iconAndDescription(l config.LSP, t *styles.Theme, states map[string]app.LSPClientInfo) (lipgloss.Style, string) { + if l.LSP.Disabled { + return t.ItemOfflineIcon.Foreground(t.FgMuted), t.S().Subtle.Render("disabled") + } + + info := states[l.Name] + switch info.State { + case lsp.StateStarting: + return t.ItemBusyIcon, t.S().Subtle.Render("starting...") + case lsp.StateReady: + return t.ItemOnlineIcon, "" + case lsp.StateError: + description := t.S().Subtle.Render("error") + if info.Error != nil { + description = t.S().Subtle.Render(fmt.Sprintf("error: %s", info.Error.Error())) + } + return t.ItemErrorIcon, description + case lsp.StateDisabled: + return t.ItemOfflineIcon.Foreground(t.FgMuted), t.S().Subtle.Render("inactive") + default: + return t.ItemOfflineIcon, "" + } +} + // RenderLSPBlock renders a complete LSP block with optional truncation indicator. func RenderLSPBlock(lspClients *csync.Map[string, *lsp.Client], opts RenderOptions, showTruncationIndicator bool) string { t := styles.CurrentTheme()