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}