@@ -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
1920func 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 (), "\n MCP 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