@@ -322,6 +322,169 @@ func ListConfigPaths(ctx context.Context, configIsArray bool) ([]ConfigPath, err
322322 return paths, nil
323323}
324324
325+ func ServerMap(configPaths []ConfigPath) (map[string]any, error) {
326+ // build a server map from all of the configs
327+ serverMap := make(map[string]any)
328+
329+ for _, configPath := range configPaths {
330+ // if the configuration file doesn't exist, skip it
331+ if _, err := os.Stat(configPath.Path); err != nil {
332+ if os.IsNotExist(err) {
333+ continue
334+ }
335+ return nil, err
336+ }
337+
338+ // read the configuration file
339+ file, err := os.Open(configPath.Path)
340+ if err != nil {
341+ return nil, err
342+ }
343+ defer file.Close()
344+
345+ // parse the configuration file as JSON
346+ var data map[string]any
347+ decoder := json.NewDecoder(file)
348+ if err := decoder.Decode(&data); err != nil {
349+ return nil, fmt.Errorf("failed to parse %s: %w", configPath.Path, err)
350+ }
351+
352+ if mcpServers, ok := data[configPath.ConfigName].(map[string]any); ok {
353+ // add metadata about the tool
354+ config := make(map[string]any)
355+ config["mcpServers"] = mcpServers
356+ config["configName"] = configPath.ConfigName
357+
358+ if configPath.ToolName != "" {
359+ config["toolName"] = configPath.ToolName
360+ }
361+
362+ serverMap[configPath.Path] = config
363+
364+ // add metadata about each MCP server
365+ for name := range mcpServers {
366+ if serverMap, ok := mcpServers[name].(map[string]any); ok {
367+ server, err := configExtract(configPath, name)
368+ if err != nil {
369+ return nil, fmt.Errorf("failed to extract config for %s: %w", name, err)
370+ }
371+
372+ for key, value := range server {
373+ if key != "command" && key != "args" {
374+ serverMap[key] = value
375+ }
376+ }
377+
378+ mcpServers[name] = serverMap
379+ }
380+ }
381+ }
382+ }
383+
384+ return serverMap, nil
385+ }
386+
387+ func SelectServerAndConfig(ctx context.Context, configIsArray bool) (string, []ConfigPath, error) {
388+ server := flag.GetString(ctx, "server")
389+
390+ // Check if the user has specified any client flags
391+ configSelected := false
392+ for client := range McpClients {
393+ configSelected = configSelected || flag.GetBool(ctx, client)
394+ }
395+
396+ // if no cllent is selected, select all clients
397+ if !configSelected {
398+ for client := range McpClients {
399+ flag.SetString(ctx, client, "true")
400+ }
401+ }
402+
403+ // Get a list of config paths
404+ configPaths, err := ListConfigPaths(ctx, true)
405+ if err != nil {
406+ return "", nil, err
407+ }
408+
409+ var serverMap map[string]any
410+
411+ if len(configPaths) > 1 || server == "" {
412+ serverMap, err = ServerMap(configPaths)
413+ if err != nil {
414+ return "", nil, fmt.Errorf("failed to get server map: %w", err)
415+ }
416+ }
417+
418+ if len(configPaths) == 0 {
419+ return "", nil, errors.New("no configuration paths found")
420+ } else if len(configPaths) > 1 && !configIsArray {
421+ choices := make([]string, 0)
422+ choiceMap := make(map[int]int)
423+ for i, configPath := range configPaths {
424+ if config, ok := serverMap[configPath.Path].(map[string]any); ok {
425+ if servers, ok := config["mcpServers"].(map[string]any); ok && len(servers) > 0 {
426+ if toolName, ok := config["toolName"].(string); ok {
427+ choices = append(choices, toolName)
428+ } else {
429+ choices = append(choices, configPath.Path)
430+ }
431+ choiceMap[i] = len(choices) - 1
432+ }
433+ }
434+ }
435+
436+ index := 0
437+ if len(choices) == 0 {
438+ return "", nil, errors.New("no MCP servers found in the selected configuration files")
439+ } else if len(choices) > 1 {
440+ err := prompt.Select(ctx, &index, "Select a configuration file", "", choices...)
441+ if err != nil {
442+ return "", nil, fmt.Errorf("failed to select configuration file: %w", err)
443+ }
444+ if choiceIndex, ok := choiceMap[index]; ok {
445+ index = choiceIndex
446+ }
447+
448+ }
449+
450+ configPaths = []ConfigPath{configPaths[index]}
451+ }
452+
453+ if server == "" {
454+ if len(serverMap) == 0 {
455+ return "", configPaths, errors.New("no MCP servers found in the selected configuration files")
456+ }
457+ // Select a server from the server map
458+ var index int
459+ choices := make([]string, 0)
460+ for _, configPath := range serverMap {
461+ if config, ok := configPath.(map[string]any); ok {
462+ if servers, ok := config["mcpServers"].(map[string]any); ok {
463+ for name := range servers {
464+ choices = append(choices, name)
465+ }
466+ }
467+ }
468+ }
469+
470+ if len(choices) == 0 {
471+ return "", configPaths, errors.New("no MCP servers found in the selected configuration files")
472+ } else if len(choices) == 1 {
473+ server = choices[0]
474+ log.Debugf("Only one MCP server found: %s", server)
475+ } else {
476+ err := prompt.Select(ctx, &index, "Select a MCP server", "", choices...)
477+ if err != nil {
478+ return "", configPaths, fmt.Errorf("failed to select MCP server: %w", err)
479+ }
480+ server = choices[index]
481+ log.Debugf("Selected MCP server: %s", server)
482+ }
483+ }
484+
485+ return server, configPaths, nil
486+ }
487+
325488// UpdateConfig updates the configuration at the specified path with the MCP servers
326489func UpdateConfig(ctx context.Context, path string, configKey string, server string, command string, args []string) error {
327490 log.Debugf("Updating configuration at %s", path)
@@ -413,32 +576,9 @@ func UpdateConfig(ctx context.Context, path string, configKey string, server str
413576func runRemove(ctx context.Context) error {
414577 var err error
415578
416- configPaths, err := ListConfigPaths (ctx, true )
579+ server, configPaths, err := SelectServerAndConfig (ctx, false )
417580 if err != nil {
418581 return err
419- } else if len(configPaths) == 0 {
420- return errors.New("no configuration paths found")
421- }
422-
423- server := flag.GetString(ctx, "server")
424- if server == "" {
425- server = flag.GetString(ctx, "name")
426- if server == "" {
427- appConfig := appconfig.ConfigFromContext(ctx)
428- if appConfig == nil {
429- appName := appconfig.NameFromContext(ctx)
430- if appName == "" {
431- return errors.New("app name is required")
432- } else {
433- appConfig, err = appconfig.FromRemoteApp(ctx, appName)
434- if err != nil {
435- return err
436- }
437- }
438- }
439-
440- server = appConfig.AppName
441- }
442582 }
443583
444584 for _, configPath := range configPaths {
0 commit comments