Skip to content

Commit bc63c91

Browse files
authored
Merge pull request #207 from docker/surface-more-server-info-on-ls
update the output of "docker mcp server ls" to surface more details
2 parents b99a2f0 + f27559e commit bc63c91

File tree

6 files changed

+464
-50
lines changed

6 files changed

+464
-50
lines changed

cmd/docker-mcp/catalog/show.go

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import (
1111

1212
"github.com/docker/cli/cli/command"
1313
"github.com/mikefarah/yq/v4/pkg/yqlib"
14-
"github.com/moby/term"
1514
"gopkg.in/yaml.v3"
1615

1716
"github.com/docker/mcp-gateway/cmd/docker-mcp/hints"
17+
"github.com/docker/mcp-gateway/pkg/terminal"
1818
"github.com/docker/mcp-gateway/pkg/yq"
1919
)
2020

@@ -119,7 +119,7 @@ func Show(ctx context.Context, dockerCli command.Cli, name string, format Format
119119
}
120120
keys := getSortedKeys(registry.Registry)
121121

122-
termWidth := getTerminalWidth()
122+
termWidth := terminal.GetWidth()
123123
wrapWidth := termWidth - 10
124124
if wrapWidth < 40 {
125125
wrapWidth = 40
@@ -178,15 +178,6 @@ func isURL(fileOrURL string) bool {
178178
return strings.HasPrefix(fileOrURL, "http://") || strings.HasPrefix(fileOrURL, "https://")
179179
}
180180

181-
func getTerminalWidth() int {
182-
fd, _ := term.GetFdInfo(os.Stdout)
183-
ws, err := term.GetWinsize(fd)
184-
if err != nil {
185-
return 80
186-
}
187-
return int(ws.Width)
188-
}
189-
190181
func wrapText(text string, width int, indent string) string {
191182
words := strings.Fields(text)
192183
if len(words) == 0 {

cmd/docker-mcp/commands/server.go

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/docker/mcp-gateway/pkg/config"
1515
"github.com/docker/mcp-gateway/pkg/docker"
1616
"github.com/docker/mcp-gateway/pkg/oci"
17+
"github.com/docker/mcp-gateway/pkg/terminal"
1718
)
1819

1920
func serverCommand(docker docker.Client, dockerCli command.Cli) *cobra.Command {
@@ -29,7 +30,7 @@ func serverCommand(docker docker.Client, dockerCli command.Cli) *cobra.Command {
2930
Short: "List enabled servers",
3031
Args: cobra.NoArgs,
3132
RunE: func(cmd *cobra.Command, _ []string) error {
32-
list, err := server.List(cmd.Context(), docker)
33+
list, err := server.List(cmd.Context(), docker, outputJSON)
3334
if err != nil {
3435
return err
3536
}
@@ -43,8 +44,51 @@ func serverCommand(docker docker.Client, dockerCli command.Cli) *cobra.Command {
4344
} else if len(list) == 0 {
4445
fmt.Fprintln(cmd.OutOrStdout(), "No server is enabled")
4546
} else {
46-
fmt.Fprintln(cmd.OutOrStdout(), strings.Join(list, ", "))
47+
// Format: $ docker mcp server ls
48+
// MCP Servers (7 enabled)
49+
//
50+
// NAME SECRETS CONFIG DESCRIPTION
51+
// atlassian ✓ done ✓ done Confluence and Jira tools
52+
53+
enabledCount := len(list)
54+
fmt.Fprintf(cmd.OutOrStdout(), "\nMCP Servers (%d enabled)\n\n", enabledCount)
55+
56+
// Calculate column widths based on terminal size
57+
termWidth := terminal.GetWidthFrom(cmd.OutOrStdout())
58+
colWidths := calculateColumnWidths(termWidth)
59+
60+
// Calculate total table width (sum of columns + spaces between columns)
61+
totalWidth := colWidths.name + colWidths.oauth + colWidths.secrets + colWidths.config + colWidths.description + 4 // 4 spaces between columns
62+
63+
// Print table headers
64+
fmt.Fprintf(cmd.OutOrStdout(), "%-*s %-*s %-*s %-*s %-*s\n",
65+
colWidths.name, "NAME",
66+
colWidths.oauth, "OAUTH",
67+
colWidths.secrets, "SECRETS",
68+
colWidths.config, "CONFIG",
69+
colWidths.description, "DESCRIPTION")
70+
fmt.Fprintln(cmd.OutOrStdout(), strings.Repeat("-", totalWidth))
71+
72+
// Print entries
73+
for _, entry := range list {
74+
// Determine secrets, config, and OAuth display strings
75+
secretsText := entry.Secrets.DisplayString()
76+
configText := entry.Config.DisplayString()
77+
oauthText := entry.OAuth.DisplayString()
78+
79+
// Truncate description to fit within the available column width
80+
description := truncateString(entry.Description, colWidths.description)
81+
82+
fmt.Fprintf(cmd.OutOrStdout(), "%-*s %-*s %-*s %-*s %-*s\n",
83+
colWidths.name, truncateString(entry.Name, colWidths.name),
84+
colWidths.oauth, oauthText,
85+
colWidths.secrets, secretsText,
86+
colWidths.config, configText,
87+
colWidths.description, description)
88+
}
89+
4790
if hints.Enabled(dockerCli) {
91+
fmt.Fprintln(cmd.OutOrStdout(), "")
4892
hints.TipCyan.Fprint(cmd.OutOrStdout(), "Tip: To use these servers, connect to a client (IE: claude/cursor) with ")
4993
hints.TipCyanBoldItalic.Fprintln(cmd.OutOrStdout(), "docker mcp client connect <client-name>")
5094
fmt.Fprintln(cmd.OutOrStdout(), "")
@@ -146,3 +190,58 @@ func serverCommand(docker docker.Client, dockerCli command.Cli) *cobra.Command {
146190

147191
return cmd
148192
}
193+
194+
type columnWidths struct {
195+
name int
196+
oauth int
197+
secrets int
198+
config int
199+
description int
200+
}
201+
202+
func calculateColumnWidths(termWidth int) columnWidths {
203+
// Minimum widths for each column
204+
minWidths := columnWidths{
205+
name: 15,
206+
oauth: 10,
207+
secrets: 10,
208+
config: 10,
209+
description: 20,
210+
}
211+
212+
// Calculate minimum total width needed
213+
minTotal := minWidths.name + minWidths.oauth + minWidths.secrets + minWidths.config + minWidths.description + 4 // 4 spaces
214+
215+
// If terminal is too narrow, use minimum widths
216+
if termWidth < minTotal+20 {
217+
return minWidths
218+
}
219+
220+
// Available space after minimums and spacing
221+
available := termWidth - minTotal
222+
223+
// Allocate extra space: 50% to description, 25% to name, 25% split between oauth/secrets/config
224+
result := columnWidths{
225+
name: minWidths.name + available/4,
226+
oauth: minWidths.oauth + available/12,
227+
secrets: minWidths.secrets + available/12,
228+
config: minWidths.config + available/12,
229+
description: minWidths.description + available/2,
230+
}
231+
232+
return result
233+
}
234+
235+
func truncateString(s string, maxWidth int) string {
236+
if maxWidth <= 0 {
237+
return ""
238+
}
239+
runes := []rune(s)
240+
if len(runes) <= maxWidth {
241+
return s
242+
}
243+
if maxWidth > 3 {
244+
return string(runes[:maxWidth-3]) + "..."
245+
}
246+
return string(runes[:maxWidth])
247+
}

0 commit comments

Comments
 (0)