Skip to content

Commit 5e0aac9

Browse files
authored
add selection to various mcp commands (#4415)
* add selection to various mcp commands * fix mcp destroy config
1 parent f6cf944 commit 5e0aac9

File tree

4 files changed

+172
-49
lines changed

4 files changed

+172
-49
lines changed

internal/command/mcp/config.go

Lines changed: 164 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -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
326489
func 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
413576
func 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 {

internal/command/mcp/destroy.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func NewDestroy() *cobra.Command {
3030
Name: "server",
3131
Description: "Name of the MCP server in the MCP client configuration",
3232
},
33-
flag.String{
33+
flag.StringArray{
3434
Name: "config",
3535
Description: "Path to the MCP client configuration file",
3636
},
@@ -57,9 +57,7 @@ func runDestroy(ctx context.Context) error {
5757
appName := appconfig.NameFromContext(ctx)
5858

5959
if appName == "" {
60-
server := flag.GetString(ctx, "server")
61-
62-
configPaths, err := ListConfigPaths(ctx, true)
60+
server, configPaths, err := SelectServerAndConfig(ctx, true)
6361
if err != nil {
6462
return err
6563
}
@@ -104,7 +102,7 @@ func runDestroy(ctx context.Context) error {
104102

105103
args = []string{}
106104

107-
// Add the MCP server to the MCP client configurations
105+
// Remove the MCP server to the MCP client configurations
108106
for client := range McpClients {
109107
if flag.GetBool(ctx, client) {
110108
args = append(args, "--"+client)
@@ -113,7 +111,7 @@ func runDestroy(ctx context.Context) error {
113111

114112
for _, config := range flag.GetStringArray(ctx, "config") {
115113
if config != "" {
116-
log.Debugf("Adding %s to MCP client configuration", config)
114+
log.Debugf("Removing %s from the MCP client configuration", config)
117115
args = append(args, "--config", config)
118116
}
119117
}

internal/command/mcp/logs.go

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -53,31 +53,18 @@ func newLogs() *cobra.Command {
5353
}
5454

5555
func runLogs(ctx context.Context) error {
56-
// Get a list of config paths
57-
configPaths, err := ListConfigPaths(ctx, true)
56+
name, configPaths, err := SelectServerAndConfig(ctx, false)
5857
if err != nil {
5958
return err
6059
}
6160

62-
if len(configPaths) == 0 {
63-
return fmt.Errorf("no MCP client configuration files found")
64-
} else if len(configPaths) > 1 {
65-
return fmt.Errorf("multiple MCP client configuration files found, please specify one")
66-
}
67-
68-
// Get the server name
69-
name := flag.GetString(ctx, "server")
70-
if name == "" {
71-
return fmt.Errorf("please specify a server name")
72-
}
73-
7461
server, err := configExtract(configPaths[0], name)
7562
if err != nil {
7663
return err
7764
}
7865

79-
if app, ok := server["app"]; ok {
80-
args := []string{"logs", "--app", app.(string)}
66+
if app, ok := server["app"].(string); ok {
67+
args := []string{"logs", "--app", app}
8168

8269
if flag.GetBool(ctx, "json") {
8370
args = append(args, "--json")

internal/command/mcp/proxy.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,7 @@ func runProxy(ctx context.Context) error {
154154
func runInspect(ctx context.Context) error {
155155
proxyInfo := getInfo(ctx)
156156

157-
server := flag.GetString(ctx, "server")
158-
159-
configPaths, err := ListConfigPaths(ctx, true)
157+
server, configPaths, err := SelectServerAndConfig(ctx, false)
160158
if err != nil {
161159
return err
162160
}

0 commit comments

Comments
 (0)