mcp.go

 1package model
 2
 3import (
 4	"fmt"
 5	"strings"
 6
 7	"charm.land/lipgloss/v2"
 8	"github.com/charmbracelet/crush/internal/agent/tools/mcp"
 9	"github.com/charmbracelet/crush/internal/ui/common"
10	"github.com/charmbracelet/crush/internal/ui/styles"
11)
12
13type MCPInfo struct {
14	mcp.ClientInfo
15}
16
17func (m *UI) mcpInfo(t *styles.Styles, width, height int) string {
18	var mcps []MCPInfo
19
20	for _, state := range m.mcpStates {
21		mcps = append(mcps, MCPInfo{ClientInfo: state})
22	}
23
24	title := t.Subtle.Render("MCPs")
25	list := t.Subtle.Render("None")
26	if len(mcps) > 0 {
27		height = max(0, height-2) // remove title and space
28		list = mcpList(t, mcps, width, height)
29	}
30
31	return lipgloss.NewStyle().Width(width).Render(fmt.Sprintf("%s\n\n%s", title, list))
32}
33
34func mcpCounts(t *styles.Styles, counts mcp.Counts) string {
35	parts := []string{}
36	if counts.Tools > 0 {
37		parts = append(parts, t.Subtle.Render(fmt.Sprintf("%d tools", counts.Tools)))
38	}
39	if counts.Prompts > 0 {
40		parts = append(parts, t.Subtle.Render(fmt.Sprintf("%d prompts", counts.Prompts)))
41	}
42	return strings.Join(parts, " ")
43}
44
45func mcpList(t *styles.Styles, mcps []MCPInfo, width, height int) string {
46	var renderedMcps []string
47	for _, m := range mcps {
48		var icon string
49		title := m.Name
50		var description string
51		var extraContent string
52
53		switch m.State {
54		case mcp.StateStarting:
55			icon = t.ItemBusyIcon.String()
56			description = t.Subtle.Render("starting...")
57		case mcp.StateConnected:
58			icon = t.ItemOnlineIcon.String()
59			extraContent = mcpCounts(t, m.Counts)
60		case mcp.StateError:
61			icon = t.ItemErrorIcon.String()
62			description = t.Subtle.Render("error")
63			if m.Error != nil {
64				description = t.Subtle.Render(fmt.Sprintf("error: %s", m.Error.Error()))
65			}
66		case mcp.StateDisabled:
67			icon = t.ItemOfflineIcon.Foreground(t.Muted.GetBackground()).String()
68			description = t.Subtle.Render("disabled")
69		default:
70			icon = t.ItemOfflineIcon.String()
71		}
72
73		renderedMcps = append(renderedMcps, common.Status(t, common.StatusOpts{
74			Icon:         icon,
75			Title:        title,
76			Description:  description,
77			ExtraContent: extraContent,
78		}, width))
79	}
80
81	if len(renderedMcps) > height {
82		visibleItems := renderedMcps[:height-1]
83		remaining := len(renderedMcps) - (height - 1)
84		visibleItems = append(visibleItems, t.Subtle.Render(fmt.Sprintf("…and %d more", remaining)))
85		return lipgloss.JoinVertical(lipgloss.Left, visibleItems...)
86	}
87	return lipgloss.JoinVertical(lipgloss.Left, renderedMcps...)
88}