Skip to content

Commit 64b7ee5

Browse files
upgrade mcp-go to v0.29.0 (#1372)
* upgrade mcp-go to v0.29.0 * upgrade nix shell go mod vendorHash * Apply suggestions from code review Co-authored-by: Eric Liu <[email protected]> --------- Co-authored-by: Eric Liu <[email protected]>
1 parent 5a6c476 commit 64b7ee5

File tree

11 files changed

+153
-113
lines changed

11 files changed

+153
-113
lines changed

pkgs/defang/cli.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ buildGoModule {
77
pname = "defang-cli";
88
version = "git";
99
src = ../../src;
10-
vendorHash = "sha256-SfjkSc0Upaa12+GrRCpEqCSp4+6F/J+jEzVoE2ELdNY="; # TODO: use fetchFromGitHub
10+
vendorHash = "sha256-+tvPdYAtGhpiXs6YGseCYrAzxIWgwGpnABxLXqYZp2k="; # TODO: use fetchFromGitHub
1111

1212
subPackages = [ "cmd/cli" ];
1313

src/go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ require (
4141
github.com/gorilla/websocket v1.5.0
4242
github.com/hashicorp/go-retryablehttp v0.7.7
4343
github.com/hexops/gotextdiff v1.0.3
44-
github.com/mark3labs/mcp-go v0.21.0
44+
github.com/mark3labs/mcp-go v0.29.0
4545
github.com/miekg/dns v1.1.59
4646
github.com/moby/patternmatcher v0.6.0
4747
github.com/muesli/termenv v0.15.2
@@ -101,6 +101,7 @@ require (
101101
github.com/russross/blackfriday/v2 v2.1.0 // indirect
102102
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
103103
github.com/sergi/go-diff v1.3.1 // indirect
104+
github.com/spf13/cast v1.7.1 // indirect
104105
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
105106
github.com/stretchr/objx v0.5.2 // indirect
106107
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect

src/go.sum

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
151151
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
152152
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
153153
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
154+
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
155+
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
154156
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
155157
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
156158
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -224,8 +226,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
224226
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
225227
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
226228
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
227-
github.com/mark3labs/mcp-go v0.21.0 h1:oyEtiXg8PnrVEFis9b1AwbiUWF2dTbyBP5yLo7SruXE=
228-
github.com/mark3labs/mcp-go v0.21.0/go.mod h1:KmJndYv7GIgcPVwEKJjNcbhVQ+hJGJhrCCB/9xITzpE=
229+
github.com/mark3labs/mcp-go v0.29.0 h1:sH1NBcumKskhxqYzhXfGc201D7P76TVXiT0fGVhabeI=
230+
github.com/mark3labs/mcp-go v0.29.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4=
229231
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
230232
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
231233
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
@@ -282,6 +284,8 @@ github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
282284
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
283285
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
284286
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
287+
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
288+
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
285289
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
286290
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
287291
github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE=

src/pkg/mcp/tools/common.go

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,28 @@ import (
1313
)
1414

1515
func configureLoader(request mcp.CallToolRequest) *compose.Loader {
16-
composeFilePaths, composeFilePathOK := request.Params.Arguments["compose_file_paths"].([]string)
17-
18-
projectName, projectNameOK := request.Params.Arguments["project_name"].(string)
16+
projectName, err := request.RequireString("project_name")
17+
if err == nil {
18+
term.Debugf("Project name provided: %s", projectName)
19+
term.Debug("Function invoked: compose.NewLoader")
20+
return compose.NewLoader(compose.WithProjectName(projectName))
21+
}
22+
arguments := request.GetArguments()
23+
composeFilePathsArgs, ok := arguments["compose_file_paths"]
24+
if ok {
25+
composeFilePaths, ok := composeFilePathsArgs.([]string)
26+
if ok {
27+
term.Debugf("Compose file paths provided: %s", composeFilePaths)
28+
term.Debug("Function invoked: compose.NewLoader")
29+
return compose.NewLoader(compose.WithPath(composeFilePaths...))
30+
}
31+
}
1932

2033
//TODO: Talk about using both project name and compose file paths
21-
2234
// if projectNameOK && composeFilePathOK {
2335
// term.Infof("Compose file paths and project name provided: %s, %s", composeFilePaths, projectName)
2436
// return compose.NewLoader(compose.WithProjectName(projectName), compose.WithPath(composeFilePaths...))
25-
if projectNameOK {
26-
term.Debugf("Project name provided: %s", projectName)
27-
term.Debug("Function invoked: compose.NewLoader")
28-
return compose.NewLoader(compose.WithProjectName(projectName))
29-
} else if composeFilePathOK {
30-
term.Debugf("Compose file paths provided: %s", composeFilePaths)
31-
term.Debug("Function invoked: compose.NewLoader")
32-
return compose.NewLoader(compose.WithPath(composeFilePaths...))
33-
}
37+
// }
3438

3539
term.Debug("Function invoked: compose.NewLoader")
3640
return compose.NewLoader()

src/pkg/mcp/tools/deploy.go

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,16 @@ func setupDeployTool(s *server.MCPServer, cluster string, providerId cliClient.P
3737
term.Debug("Compose up tool called - deploying services")
3838
track.Evt("MCP Deploy Tool")
3939

40-
wd, ok := request.Params.Arguments["working_directory"].(string)
41-
if ok && wd != "" {
42-
err := os.Chdir(wd)
43-
if err != nil {
44-
term.Error("Failed to change working directory", "error", err)
45-
}
40+
wd, err := request.RequireString("working_directory")
41+
if err != nil || wd == "" {
42+
term.Error("Invalid working directory", "error", errors.New("working_directory is required"))
43+
return mcp.NewToolResultErrorFromErr("Invalid working directory", errors.New("working_directory is required")), err
44+
}
45+
46+
err = os.Chdir(wd)
47+
if err != nil {
48+
term.Error("Failed to change working directory", "error", err)
49+
return mcp.NewToolResultErrorFromErr("Failed to change working directory", err), err
4650
}
4751

4852
loader := configureLoader(request)
@@ -53,13 +57,13 @@ func setupDeployTool(s *server.MCPServer, cluster string, providerId cliClient.P
5357
err = fmt.Errorf("failed to parse compose file: %w", err)
5458
term.Error("Failed to deploy services", "error", err)
5559

56-
return mcp.NewToolResultText(fmt.Sprintf("Local deployment failed: %v. Please provide a valid compose file path.", err)), nil
60+
return mcp.NewToolResultText(fmt.Sprintf("Local deployment failed: %v. Please provide a valid compose file path.", err)), err
5761
}
5862

5963
term.Debug("Function invoked: cli.Connect")
6064
client, err := cli.Connect(ctx, cluster)
6165
if err != nil {
62-
return mcp.NewToolResultErrorFromErr("Could not connect", err), nil
66+
return mcp.NewToolResultErrorFromErr("Could not connect", err), err
6367
}
6468

6569
client.Track("MCP Deploy Tool")
@@ -68,13 +72,13 @@ func setupDeployTool(s *server.MCPServer, cluster string, providerId cliClient.P
6872
provider, err := cli.NewProvider(ctx, providerId, client)
6973
if err != nil {
7074
term.Error("Failed to get new provider", "error", err)
71-
return mcp.NewToolResultErrorFromErr("Failed to get new provider", err), nil
75+
return mcp.NewToolResultErrorFromErr("Failed to get new provider", err), err
7276
}
7377

7478
err = canIUseProvider(ctx, client, project.Name, provider)
7579
if err != nil {
7680
term.Error("Failed to use provider", "error", err)
77-
return mcp.NewToolResultErrorFromErr("Failed to use provider", err), nil
81+
return mcp.NewToolResultErrorFromErr("Failed to use provider", err), err
7882
}
7983

8084
// Deploy the services
@@ -89,14 +93,14 @@ func setupDeployTool(s *server.MCPServer, cluster string, providerId cliClient.P
8993

9094
result := HandleTermsOfServiceError(err)
9195
if result != nil {
92-
return result, nil
96+
return result, err
9397
}
9498
result = HandleConfigError(err)
9599
if result != nil {
96-
return result, nil
100+
return result, err
97101
}
98102

99-
return mcp.NewToolResultErrorFromErr("Failed to compose up services", err), nil
103+
return mcp.NewToolResultErrorFromErr("Failed to compose up services", err), err
100104
}
101105

102106
if len(deployResp.Services) == 0 {

src/pkg/mcp/tools/destroy.go

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package tools
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"os"
78

@@ -36,7 +37,7 @@ func setupDestroyTool(s *server.MCPServer, cluster string, providerId cliClient.
3637
term.Debug("Function invoked: cli.Connect")
3738
client, err := cli.Connect(ctx, cluster)
3839
if err != nil {
39-
return mcp.NewToolResultErrorFromErr("Could not connect", err), nil
40+
return mcp.NewToolResultErrorFromErr("Could not connect", err), err
4041
}
4142

4243
client.Track("MCP Destroy Tool")
@@ -45,15 +46,19 @@ func setupDestroyTool(s *server.MCPServer, cluster string, providerId cliClient.
4546
provider, err := cli.NewProvider(ctx, providerId, client)
4647
if err != nil {
4748
term.Error("Failed to get new provider", "error", err)
48-
return mcp.NewToolResultErrorFromErr("Failed to get new provider", err), nil
49+
return mcp.NewToolResultErrorFromErr("Failed to get new provider", err), err
4950
}
5051

51-
wd, ok := request.Params.Arguments["working_directory"].(string)
52-
if !ok || wd != "" {
53-
err := os.Chdir(wd)
54-
if err != nil {
55-
term.Error("Failed to change working directory", "error", err)
56-
}
52+
wd, err := request.RequireString("working_directory")
53+
if err != nil || wd == "" {
54+
term.Error("Invalid working directory", "error", errors.New("working_directory is required"))
55+
return mcp.NewToolResultErrorFromErr("Invalid working directory", errors.New("working_directory is required")), err
56+
}
57+
58+
err = os.Chdir(wd)
59+
if err != nil {
60+
term.Error("Failed to change working directory", "error", err)
61+
return mcp.NewToolResultErrorFromErr("Failed to change working directory", err), err
5762
}
5863

5964
loader := configureLoader(request)
@@ -62,13 +67,13 @@ func setupDestroyTool(s *server.MCPServer, cluster string, providerId cliClient.
6267
projectName, err := cliClient.LoadProjectNameWithFallback(ctx, loader, provider)
6368
if err != nil {
6469
term.Error("Failed to load project name", "error", err)
65-
return mcp.NewToolResultErrorFromErr("Failed to load project name", err), nil
70+
return mcp.NewToolResultErrorFromErr("Failed to load project name", err), err
6671
}
6772

6873
err = canIUseProvider(ctx, client, projectName, provider)
6974
if err != nil {
7075
term.Error("Failed to use provider", "error", err)
71-
return mcp.NewToolResultErrorFromErr("Failed to use provider", err), nil
76+
return mcp.NewToolResultErrorFromErr("Failed to use provider", err), err
7277
}
7378

7479
term.Debug("Function invoked: cli.ComposeDown")
@@ -77,15 +82,15 @@ func setupDestroyTool(s *server.MCPServer, cluster string, providerId cliClient.
7782
if connect.CodeOf(err) == connect.CodeNotFound {
7883
// Show a warning (not an error) if the service was not found
7984
term.Warn("Project not found", "error", err)
80-
return mcp.NewToolResultText("Project not found, nothing to destroy. Please use a valid project name, compose file path or project directory."), nil
85+
return mcp.NewToolResultText("Project not found, nothing to destroy. Please use a valid project name, compose file path or project directory."), err
8186
}
8287

8388
result := HandleTermsOfServiceError(err)
8489
if result != nil {
85-
return result, nil
90+
return result, err
8691
}
8792

88-
return mcp.NewToolResultErrorFromErr("Failed to destroy project", err), nil
93+
return mcp.NewToolResultErrorFromErr("Failed to destroy project", err), err
8994
}
9095

9196
return mcp.NewToolResultText(fmt.Sprintf("Successfully destroyed project: %s, etag: %s", projectName, deployment)), nil

src/pkg/mcp/tools/estimate.go

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package tools
33
import (
44
"bytes"
55
"context"
6+
"errors"
67
"fmt"
78
"os"
89
"strings"
@@ -50,21 +51,25 @@ func setupEstimateTool(s *server.MCPServer, cluster string, providerId cliClient
5051
term.Debug("Estimate tool called")
5152
track.Evt("MCP Estimate Tool")
5253

53-
wd, ok := request.Params.Arguments["working_directory"].(string)
54-
if ok && wd != "" {
55-
err := os.Chdir(wd)
56-
if err != nil {
57-
term.Error("Failed to change working directory", "error", err)
58-
}
54+
wd, err := request.RequireString("working_directory")
55+
if err != nil || wd == "" {
56+
term.Error("Invalid working directory", "error", errors.New("working_directory is required"))
57+
return mcp.NewToolResultErrorFromErr("Invalid working directory", errors.New("working_directory is required")), err
5958
}
6059

61-
modeString, ok := request.Params.Arguments["deployment_mode"].(string)
62-
if !ok {
60+
err = os.Chdir(wd)
61+
if err != nil {
62+
term.Error("Failed to change working directory", "error", err)
63+
return mcp.NewToolResultErrorFromErr("Failed to change working directory", err), err
64+
}
65+
66+
modeString, err := request.RequireString("deployment_mode")
67+
if err != nil {
6368
modeString = "AFFORDABLE" // Default to AFFORDABLE if not provided
6469
}
6570

66-
providerString, ok := request.Params.Arguments["provider"].(string)
67-
if !ok {
71+
providerString, err := request.RequireString("provider")
72+
if err != nil {
6873
providerString = providerId.String()
6974
}
7075

@@ -92,15 +97,15 @@ func setupEstimateTool(s *server.MCPServer, cluster string, providerId cliClient
9297
project, err := loader.LoadProject(ctx)
9398
if err != nil {
9499
err = fmt.Errorf("failed to parse compose file: %w", err)
95-
term.Error("Failed to deploy services", "error", err)
100+
term.Error("failed to parse compose file", "error", err)
96101

97-
return mcp.NewToolResultText(fmt.Sprintf("Estimate failed: %v. Please provide a valid compose file path.", err)), nil
102+
return mcp.NewToolResultErrorFromErr("failed to parse compose file", err), err
98103
}
99104

100105
term.Debug("Function invoked: cli.Connect")
101106
client, err := cli.Connect(ctx, cluster)
102107
if err != nil {
103-
return mcp.NewToolResultErrorFromErr("Could not connect", err), nil
108+
return mcp.NewToolResultErrorFromErr("Could not connect", err), err
104109
}
105110

106111
defangProvider := &cliClient.PlaygroundProvider{FabricClient: client}
@@ -109,7 +114,7 @@ func setupEstimateTool(s *server.MCPServer, cluster string, providerId cliClient
109114
err = providerID.Set(providerString)
110115
if err != nil {
111116
term.Error("Invalid provider specified", "error", err)
112-
return mcp.NewToolResultErrorFromErr("Invalid provider specified", err), nil
117+
return mcp.NewToolResultErrorFromErr("Invalid provider specified", err), err
113118
}
114119

115120
term.Debug("Function invoked: cli.RunEstimate")
@@ -120,7 +125,7 @@ func setupEstimateTool(s *server.MCPServer, cluster string, providerId cliClient
120125

121126
estimate, err := cli.RunEstimate(ctx, project, client, defangProvider, providerID, region, mode)
122127
if err != nil {
123-
return mcp.NewToolResultErrorFromErr("Failed to run estimate", err), nil
128+
return mcp.NewToolResultErrorFromErr("Failed to run estimate", err), err
124129
}
125130
term.Debugf("Estimate: %+v", estimate)
126131

src/pkg/mcp/tools/listConfig.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package tools
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"os"
78
"strings"
@@ -33,42 +34,46 @@ func setupListConfigTool(s *server.MCPServer, cluster string, providerId cliClie
3334
term.Debug("List Config tool called")
3435
track.Evt("MCP List Config Tool")
3536

36-
wd, ok := request.Params.Arguments["working_directory"].(string)
37-
if ok && wd != "" {
38-
err := os.Chdir(wd)
39-
if err != nil {
40-
term.Error("Failed to change working directory", "error", err)
41-
}
37+
wd, err := request.RequireString("working_directory")
38+
if err != nil || wd == "" {
39+
term.Error("Invalid working directory", "error", errors.New("working_directory is required"))
40+
return mcp.NewToolResultErrorFromErr("Invalid working directory", errors.New("working_directory is required")), err
41+
}
42+
43+
err = os.Chdir(wd)
44+
if err != nil {
45+
term.Error("Failed to change working directory", "error", err)
46+
return mcp.NewToolResultErrorFromErr("Failed to change working directory", err), err
4247
}
4348

4449
term.Debug("Function invoked: cli.Connect")
4550
client, err := cli.Connect(ctx, cluster)
4651
if err != nil {
47-
return mcp.NewToolResultErrorFromErr("Could not connect", err), nil
52+
return mcp.NewToolResultErrorFromErr("Could not connect", err), err
4853
}
4954

5055
term.Debug("Function invoked: cli.NewProvider")
5156
provider, err := cli.NewProvider(ctx, providerId, client)
5257
if err != nil {
5358
term.Error("Failed to get new provider", "error", err)
5459

55-
return mcp.NewToolResultErrorFromErr("Failed to get new provider", err), nil
60+
return mcp.NewToolResultErrorFromErr("Failed to get new provider", err), err
5661
}
5762

5863
loader := configureLoader(request)
5964

6065
term.Debug("Function invoked: cliClient.LoadProjectNameWithFallback")
6166
projectName, err := cliClient.LoadProjectNameWithFallback(ctx, loader, provider)
6267
if err != nil {
63-
return mcp.NewToolResultErrorFromErr("Failed to load project name", err), nil
68+
return mcp.NewToolResultErrorFromErr("Failed to load project name", err), err
6469
}
6570
term.Debug("Project name loaded:", projectName)
6671

6772
term.Debug("Function invoked: cli.ConfigList")
6873

6974
config, err := provider.ListConfig(ctx, &defangv1.ListConfigsRequest{Project: projectName})
7075
if err != nil {
71-
return mcp.NewToolResultErrorFromErr("Failed to list config variables", err), nil
76+
return mcp.NewToolResultErrorFromErr("Failed to list config variables", err), err
7277
}
7378

7479
numConfigs := len(config.Names)

0 commit comments

Comments
 (0)