Skip to content

Commit 1da0980

Browse files
fix: add JSON/YAML output support to auth status command (smart-mcp-proxy#270)
Refactored runAuthStatusClientMode() to support standard CLI output formatting (--json, --yaml, -o flags) consistent with other commands. Changes: - Extract OAuth server filtering into filterOAuthServers() helper - Extract pretty display logic into displayAuthStatusPretty() function - Use GetOutputFormatter() and ResolveOutputFormat() for format detection - Output raw JSON/YAML for structured formats - Preserve existing human-readable output for table format Tests: - Added TestFilterOAuthServers() with 5 test cases covering OAuth server detection logic (oauth config, authenticated status, errors) Fixes: mcpproxy-go-yv4v Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent d30d95f commit 1da0980

2 files changed

Lines changed: 129 additions & 15 deletions

File tree

cmd/mcpproxy/auth_cmd.go

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -361,15 +361,35 @@ func runAuthStatusClientMode(ctx context.Context, dataDir, serverName string, al
361361
}
362362
}
363363

364-
// Display OAuth status
365-
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
366-
fmt.Println("🔐 OAuth Authentication Status")
367-
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
368-
fmt.Println()
364+
// Filter to only OAuth servers
365+
oauthServers := filterOAuthServers(servers)
366+
367+
// Get the output formatter based on global flags
368+
formatter, err := GetOutputFormatter()
369+
if err != nil {
370+
return fmt.Errorf("failed to get output formatter: %w", err)
371+
}
372+
373+
outputFormat := ResolveOutputFormat()
374+
375+
// For structured formats (json, yaml), output raw data
376+
if outputFormat == "json" || outputFormat == "yaml" {
377+
result, err := formatter.Format(oauthServers)
378+
if err != nil {
379+
return fmt.Errorf("failed to format output: %w", err)
380+
}
381+
fmt.Println(result)
382+
return nil
383+
}
369384

370-
hasOAuthServers := false
385+
// For table format, use human-readable display
386+
return displayAuthStatusPretty(oauthServers)
387+
}
388+
389+
// filterOAuthServers filters servers to only those with OAuth configuration
390+
func filterOAuthServers(servers []map[string]interface{}) []map[string]interface{} {
391+
var oauthServers []map[string]interface{}
371392
for _, srv := range servers {
372-
name, _ := srv["name"].(string)
373393
oauth, _ := srv["oauth"].(map[string]interface{})
374394
authenticated, _ := srv["authenticated"].(bool)
375395
lastError, _ := srv["last_error"].(string)
@@ -382,11 +402,30 @@ func runAuthStatusClientMode(ctx context.Context, dataDir, serverName string, al
382402
containsIgnoreCase(lastError, "oauth") ||
383403
authenticated
384404

385-
if !isOAuthServer {
386-
continue // Skip non-OAuth servers
405+
if isOAuthServer {
406+
oauthServers = append(oauthServers, srv)
387407
}
408+
}
409+
return oauthServers
410+
}
388411

389-
hasOAuthServers = true
412+
// displayAuthStatusPretty displays OAuth status in human-readable format
413+
func displayAuthStatusPretty(servers []map[string]interface{}) error {
414+
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
415+
fmt.Println("🔐 OAuth Authentication Status")
416+
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
417+
fmt.Println()
418+
419+
if len(servers) == 0 {
420+
fmt.Println("ℹ️ No servers with OAuth configuration found.")
421+
fmt.Println(" Configure OAuth in mcp_config.json to enable authentication.")
422+
return nil
423+
}
424+
425+
for _, srv := range servers {
426+
name, _ := srv["name"].(string)
427+
oauth, _ := srv["oauth"].(map[string]interface{})
428+
lastError, _ := srv["last_error"].(string)
390429

391430
// Use unified health status from backend (FR-006, FR-007)
392431
var healthLevel, adminState, healthSummary, healthAction string
@@ -552,11 +591,6 @@ func runAuthStatusClientMode(ctx context.Context, dataDir, serverName string, al
552591
fmt.Println()
553592
}
554593

555-
if !hasOAuthServers {
556-
fmt.Println("ℹ️ No servers with OAuth configuration found.")
557-
fmt.Println(" Configure OAuth in mcp_config.json to enable authentication.")
558-
}
559-
560594
return nil
561595
}
562596

cmd/mcpproxy/auth_cmd_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,83 @@ func TestAuthLogin_ForceFlagWithoutAll(t *testing.T) {
181181
assert.NotContains(t, err.Error(), "either --server or --all")
182182
}
183183
}
184+
185+
func TestFilterOAuthServers(t *testing.T) {
186+
tests := []struct {
187+
name string
188+
servers []map[string]interface{}
189+
expected int
190+
}{
191+
{
192+
name: "filter servers with OAuth config",
193+
servers: []map[string]interface{}{
194+
{
195+
"name": "oauth-server",
196+
"oauth": map[string]interface{}{"client_id": "test"},
197+
},
198+
{
199+
"name": "non-oauth-server",
200+
},
201+
},
202+
expected: 1,
203+
},
204+
{
205+
name: "filter servers with authenticated status",
206+
servers: []map[string]interface{}{
207+
{
208+
"name": "authenticated-server",
209+
"authenticated": true,
210+
},
211+
{
212+
"name": "non-authenticated-server",
213+
"authenticated": false,
214+
},
215+
},
216+
expected: 1,
217+
},
218+
{
219+
name: "filter servers with OAuth errors",
220+
servers: []map[string]interface{}{
221+
{
222+
"name": "error-server",
223+
"last_error": "OAuth authentication required",
224+
},
225+
{
226+
"name": "other-error-server",
227+
"last_error": "Connection refused",
228+
},
229+
},
230+
expected: 1,
231+
},
232+
{
233+
name: "empty server list",
234+
servers: []map[string]interface{}{},
235+
expected: 0,
236+
},
237+
{
238+
name: "all OAuth servers",
239+
servers: []map[string]interface{}{
240+
{
241+
"name": "oauth1",
242+
"oauth": map[string]interface{}{"client_id": "test1"},
243+
},
244+
{
245+
"name": "oauth2",
246+
"authenticated": true,
247+
},
248+
{
249+
"name": "oauth3",
250+
"last_error": "OAuth error",
251+
},
252+
},
253+
expected: 3,
254+
},
255+
}
256+
257+
for _, tt := range tests {
258+
t.Run(tt.name, func(t *testing.T) {
259+
result := filterOAuthServers(tt.servers)
260+
assert.Equal(t, tt.expected, len(result), "filterOAuthServers should return correct number of OAuth servers")
261+
})
262+
}
263+
}

0 commit comments

Comments
 (0)