From 12e056f583ec20c04c3c0c4ff783fa93513f5511 Mon Sep 17 00:00:00 2001 From: Jeff Haynie Date: Wed, 11 Jun 2025 16:07:43 -0500 Subject: [PATCH 1/5] Add AMP MCP support --- internal/mcp/config.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/internal/mcp/config.go b/internal/mcp/config.go index e17eda81..ce9f32fa 100644 --- a/internal/mcp/config.go +++ b/internal/mcp/config.go @@ -39,6 +39,8 @@ type MCPClientConfig struct { Config *MCPConfig `json:"-"` Detected bool `json:"-"` // if the agentuity mcp server is detected in the config file Installed bool `json:"-"` // if this client is installed on this machine + + BeforeSaveHook func(content []byte) ([]byte, error) `json:"-"` } type MCPServerConfig struct { @@ -50,6 +52,8 @@ type MCPServerConfig struct { type MCPConfig struct { MCPServers map[string]MCPServerConfig `json:"mcpServers"` filename string + + client *MCPClientConfig `json:"-"` } func (c *MCPConfig) AddIfNotExists(name string, command string, args []any, env map[string]any) bool { @@ -76,6 +80,14 @@ func (c *MCPConfig) Save() error { if err != nil { return err } + if c.client != nil { + if c.client.BeforeSaveHook != nil { + content, err = c.client.BeforeSaveHook(content) + if err != nil { + return err + } + } + } return os.WriteFile(c.filename, content, 0644) } @@ -215,6 +227,7 @@ func Install(ctx context.Context, logger logger.Logger) error { if config.Transport == "" { config.Transport = "stdio" } + config.Config.client = &config if config.Config.AddIfNotExists(agentuityToolName, executable, append(agentuityToolArgs, "--"+config.Transport), agentuityToolEnv) { if err := config.Config.Save(); err != nil { return fmt.Errorf("failed to save config for %s: %w", config.Name, err) @@ -373,4 +386,19 @@ func init() { Linux: []string{"/usr/bin/anthropic", "/usr/local/bin/anthropic", "$PATH"}, }, }) + mcpClientConfigs = append(mcpClientConfigs, MCPClientConfig{ + Name: "Amp", + ConfigLocation: "$HOME/.config/amp/settings.json", + Command: "amp", + Transport: "stdio", + Application: &MCPClientApplicationConfig{ + MacOS: []string{"/usr/local/bin/amp", "/opt/homebrew/bin/amp", "$PATH"}, + Linux: []string{"/usr/bin/amp", "/usr/local/bin/amp", "$PATH"}, + }, + BeforeSaveHook: func(content []byte) ([]byte, error) { + buf := string(content) + buf = strings.Replace(buf, "mcpServers", "amp.mcpServers", 1) + return []byte(buf), nil + }, + }) } From 82b17dee01b0d0366759fb1c5ddaa63ed208a04c Mon Sep 17 00:00:00 2001 From: Jeff Haynie Date: Wed, 11 Jun 2025 16:15:46 -0500 Subject: [PATCH 2/5] bump to get fix --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index d2f19471..4575f358 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/agentuity/cli -go 1.24.2 +go 1.24.4 require ( github.com/Masterminds/semver v1.5.0 From e3a3c8115773b8f9ddff3a7214814a0830bc5ff4 Mon Sep 17 00:00:00 2001 From: Jeff Haynie Date: Wed, 11 Jun 2025 16:18:49 -0500 Subject: [PATCH 3/5] a few safety changes --- internal/mcp/config.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/mcp/config.go b/internal/mcp/config.go index ce9f32fa..c865a60e 100644 --- a/internal/mcp/config.go +++ b/internal/mcp/config.go @@ -239,6 +239,7 @@ func Install(ctx context.Context, logger logger.Logger) error { tui.ShowSuccess("Agentuity MCP server already installed for %s", config.Name) } installed++ + config.Config.client = nil } if installed == 0 { tui.ShowSuccess("All MCP clients are up-to-date") @@ -274,9 +275,11 @@ func Uninstall(ctx context.Context, logger logger.Logger) error { continue } delete(mcpconfig.MCPServers, agentuityToolName) + mcpconfig.client = &config if err := mcpconfig.Save(); err != nil { return fmt.Errorf("failed to save config for %s: %w", config.Name, err) } + mcpconfig.client = nil logger.Debug("removed %s config for %s at %s", agentuityToolName, config.Name, config.ConfigLocation) tui.ShowSuccess("Uninstalled Agentuity MCP server for %s", config.Name) uninstalled++ From c8bbd99edbb6ecd3121ab0a1fc1df7ef12d37951 Mon Sep 17 00:00:00 2001 From: Jeff Haynie Date: Wed, 11 Jun 2025 16:24:51 -0500 Subject: [PATCH 4/5] more robust handling --- internal/mcp/config.go | 69 ++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/internal/mcp/config.go b/internal/mcp/config.go index c865a60e..31fd1581 100644 --- a/internal/mcp/config.go +++ b/internal/mcp/config.go @@ -39,8 +39,7 @@ type MCPClientConfig struct { Config *MCPConfig `json:"-"` Detected bool `json:"-"` // if the agentuity mcp server is detected in the config file Installed bool `json:"-"` // if this client is installed on this machine - - BeforeSaveHook func(content []byte) ([]byte, error) `json:"-"` + IsAMP bool `json:"-"` } type MCPServerConfig struct { @@ -50,20 +49,32 @@ type MCPServerConfig struct { } type MCPConfig struct { - MCPServers map[string]MCPServerConfig `json:"mcpServers"` - filename string + MCPServers map[string]MCPServerConfig `json:"mcpServers,omitempty"` + AMPMCPServers map[string]MCPServerConfig `json:"amp.mcpServers,omitempty"` + filename string client *MCPClientConfig `json:"-"` } -func (c *MCPConfig) AddIfNotExists(name string, command string, args []any, env map[string]any) bool { - if _, ok := c.MCPServers[name]; ok { - return false - } - c.MCPServers[name] = MCPServerConfig{ - Command: command, - Args: args, - Env: env, +func (c *MCPConfig) AddIfNotExists(name string, command string, args []any, env map[string]any, isAMP bool) bool { + if isAMP { + if _, ok := c.AMPMCPServers[name]; ok { + return false + } + c.AMPMCPServers[name] = MCPServerConfig{ + Command: command, + Args: args, + Env: env, + } + } else { + if _, ok := c.MCPServers[name]; ok { + return false + } + c.MCPServers[name] = MCPServerConfig{ + Command: command, + Args: args, + Env: env, + } } return true } @@ -72,7 +83,7 @@ func (c *MCPConfig) Save() error { if c.filename == "" { return errors.New("filename is not set") } - if len(c.MCPServers) == 0 { + if len(c.MCPServers) == 0 && len(c.AMPMCPServers) == 0 { os.Remove(c.filename) // if no more MCP servers, remove the config file return nil } @@ -80,14 +91,6 @@ func (c *MCPConfig) Save() error { if err != nil { return err } - if c.client != nil { - if c.client.BeforeSaveHook != nil { - content, err = c.client.BeforeSaveHook(content) - if err != nil { - return err - } - } - } return os.WriteFile(c.filename, content, 0644) } @@ -169,6 +172,9 @@ func Detect(logger logger.Logger, all bool) ([]MCPClientConfig, error) { if _, ok := mcpconfig.MCPServers[agentuityToolName]; ok { config.Detected = true } + if _, ok := mcpconfig.AMPMCPServers[agentuityToolName]; ok { + config.Detected = true + } config.Config = mcpconfig } found = append(found, config) @@ -213,8 +219,9 @@ func Install(ctx context.Context, logger logger.Logger) error { } if config.Config == nil { config.Config = &MCPConfig{ - MCPServers: make(map[string]MCPServerConfig), - filename: config.ConfigLocation, + MCPServers: make(map[string]MCPServerConfig), + AMPMCPServers: make(map[string]MCPServerConfig), + filename: config.ConfigLocation, } dir := filepath.Dir(config.ConfigLocation) if !util.Exists(dir) { @@ -228,7 +235,7 @@ func Install(ctx context.Context, logger logger.Logger) error { config.Transport = "stdio" } config.Config.client = &config - if config.Config.AddIfNotExists(agentuityToolName, executable, append(agentuityToolArgs, "--"+config.Transport), agentuityToolEnv) { + if config.Config.AddIfNotExists(agentuityToolName, executable, append(agentuityToolArgs, "--"+config.Transport), agentuityToolEnv, config.IsAMP) { if err := config.Config.Save(); err != nil { return fmt.Errorf("failed to save config for %s: %w", config.Name, err) } @@ -239,7 +246,6 @@ func Install(ctx context.Context, logger logger.Logger) error { tui.ShowSuccess("Agentuity MCP server already installed for %s", config.Name) } installed++ - config.Config.client = nil } if installed == 0 { tui.ShowSuccess("All MCP clients are up-to-date") @@ -274,12 +280,15 @@ func Uninstall(ctx context.Context, logger logger.Logger) error { logger.Debug("config for %s not found in %s, skipping", config.Name, config.ConfigLocation) continue } + if _, ok := mcpconfig.AMPMCPServers[agentuityToolName]; !ok { + logger.Debug("config for %s not found in %s, skipping", config.Name, config.ConfigLocation) + continue + } delete(mcpconfig.MCPServers, agentuityToolName) - mcpconfig.client = &config + delete(mcpconfig.AMPMCPServers, agentuityToolName) if err := mcpconfig.Save(); err != nil { return fmt.Errorf("failed to save config for %s: %w", config.Name, err) } - mcpconfig.client = nil logger.Debug("removed %s config for %s at %s", agentuityToolName, config.Name, config.ConfigLocation) tui.ShowSuccess("Uninstalled Agentuity MCP server for %s", config.Name) uninstalled++ @@ -398,10 +407,6 @@ func init() { MacOS: []string{"/usr/local/bin/amp", "/opt/homebrew/bin/amp", "$PATH"}, Linux: []string{"/usr/bin/amp", "/usr/local/bin/amp", "$PATH"}, }, - BeforeSaveHook: func(content []byte) ([]byte, error) { - buf := string(content) - buf = strings.Replace(buf, "mcpServers", "amp.mcpServers", 1) - return []byte(buf), nil - }, + IsAMP: true, }) } From 8c978b15c15d132b77f85455045e8d7e3b016388 Mon Sep 17 00:00:00 2001 From: Jeff Haynie Date: Wed, 11 Jun 2025 18:18:07 -0500 Subject: [PATCH 5/5] this is a bit gross given how AMP installs into the code forks using the settings.json vs a normal mcp config --- internal/mcp/config.go | 98 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 4 deletions(-) diff --git a/internal/mcp/config.go b/internal/mcp/config.go index 31fd1581..e08c0829 100644 --- a/internal/mcp/config.go +++ b/internal/mcp/config.go @@ -51,9 +51,66 @@ type MCPServerConfig struct { type MCPConfig struct { MCPServers map[string]MCPServerConfig `json:"mcpServers,omitempty"` AMPMCPServers map[string]MCPServerConfig `json:"amp.mcpServers,omitempty"` + AMPURL string `json:"amp.url"` filename string - client *MCPClientConfig `json:"-"` + client *MCPClientConfig `json:"-"` + Extra map[string]interface{} `json:"-"` +} + +func (c *MCPConfig) UnmarshalJSON(data []byte) error { + type Alias MCPConfig // Prevent recursion + aux := &struct { + *Alias + }{ + Alias: (*Alias)(c), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + // Unmarshal into a map to find extra fields + var all map[string]json.RawMessage + if err := json.Unmarshal(data, &all); err != nil { + return err + } + // Remove known fields + delete(all, "mcpServers") + delete(all, "amp.mcpServers") + delete(all, "amp.url") + // Store the rest in Extra + c.Extra = make(map[string]interface{}) + for k, v := range all { + var val interface{} + if err := json.Unmarshal(v, &val); err != nil { + c.Extra[k] = string(v) // fallback to raw string + } else { + c.Extra[k] = val + } + } + return nil +} + +func (c *MCPConfig) MarshalJSON() ([]byte, error) { + type Alias MCPConfig + aux := &struct { + *Alias + }{ + Alias: (*Alias)(c), + } + // Marshal known fields + data, err := json.Marshal(aux) + if err != nil { + return nil, err + } + // Unmarshal back into a map to merge with Extra + var m map[string]interface{} + if err := json.Unmarshal(data, &m); err != nil { + return nil, err + } + for k, v := range c.Extra { + m[k] = v + } + return json.MarshalIndent(m, "", " ") } func (c *MCPConfig) AddIfNotExists(name string, command string, args []any, env map[string]any, isAMP bool) bool { @@ -61,6 +118,9 @@ func (c *MCPConfig) AddIfNotExists(name string, command string, args []any, env if _, ok := c.AMPMCPServers[name]; ok { return false } + if c.AMPMCPServers == nil { + c.AMPMCPServers = make(map[string]MCPServerConfig) + } c.AMPMCPServers[name] = MCPServerConfig{ Command: command, Args: args, @@ -70,6 +130,9 @@ func (c *MCPConfig) AddIfNotExists(name string, command string, args []any, env if _, ok := c.MCPServers[name]; ok { return false } + if c.MCPServers == nil { + return false + } c.MCPServers[name] = MCPServerConfig{ Command: command, Args: args, @@ -117,7 +180,7 @@ func Detect(logger logger.Logger, all bool) ([]MCPClientConfig, error) { } var found []MCPClientConfig for _, config := range mcpClientConfigs { - var exists bool + var exists, provisionalExists bool if config.Command != "" { _, err := exec.LookPath(config.Command) if err != nil { @@ -154,6 +217,13 @@ func Detect(logger logger.Logger, all bool) ([]MCPClientConfig, error) { } } } + config.ConfigLocation = strings.Replace(config.ConfigLocation, "$HOME", home, 1) + if config.Command == "" && config.Application == nil && util.Exists(config.ConfigLocation) { + if config.IsAMP { + exists = true + provisionalExists = true + } + } if !exists { if all { found = append(found, config) @@ -161,7 +231,6 @@ func Detect(logger logger.Logger, all bool) ([]MCPClientConfig, error) { continue } config.Installed = true - config.ConfigLocation = strings.Replace(config.ConfigLocation, "$HOME", home, 1) var mcpconfig *MCPConfig if util.Exists(config.ConfigLocation) { mcpconfig, err = loadConfig(config.ConfigLocation) @@ -169,6 +238,14 @@ func Detect(logger logger.Logger, all bool) ([]MCPClientConfig, error) { logger.Error("failed to load MCP config for %s: %s", config.Name, err) return nil, nil } + if provisionalExists && mcpconfig.AMPURL == "" { + config.Installed = false + config.Detected = false + if all { + found = append(found, config) + } + continue + } if _, ok := mcpconfig.MCPServers[agentuityToolName]; ok { config.Detected = true } @@ -241,7 +318,7 @@ func Install(ctx context.Context, logger logger.Logger) error { } logger.Debug("added %s config for %s at %s", agentuityToolName, config.Name, config.ConfigLocation) tui.ShowSuccess("Installed Agentuity MCP server for %s", config.Name) - } else { + } else if !config.IsAMP { logger.Debug("config for %s already exists at %s", agentuityToolName, config.ConfigLocation) tui.ShowSuccess("Agentuity MCP server already installed for %s", config.Name) } @@ -409,4 +486,17 @@ func init() { }, IsAMP: true, }) + for fork, name := range map[string]string{ + "VS Code": "Code", + "Cursor": "Cursor", + "Windsurf": "Windsurf", + "Cline": "Cline", + } { + mcpClientConfigs = append(mcpClientConfigs, MCPClientConfig{ + Name: "Amp (" + fork + ")", + ConfigLocation: filepath.Join(util.GetAppSupportDir(name), "User", "settings.json"), + Transport: "stdio", + IsAMP: true, + }) + } }