-
Notifications
You must be signed in to change notification settings - Fork 165
WIP: Generic Tool Abstraction #210
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Signed-off-by: Andrea Luzzardi <[email protected]>
eadc258 to
282173e
Compare
Signed-off-by: Andrea Luzzardi <[email protected]>
| ActualHandler func(ctx context.Context, request *R) (*ToolResponse[V], error) | ||
| Handler server.ToolHandlerFunc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| ActualHandler func(ctx context.Context, request *R) (*ToolResponse[V], error) | |
| Handler server.ToolHandlerFunc | |
| Handler func(ctx context.Context, request *R) (*ToolResponse[V], error) | |
| MCPHandler server.ToolHandlerFunc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤖 Code Review
Summary
This PR introduces a generic tool abstraction for the MCP server, refactoring EnvironmentCreateTool and EnvironmentFileListTool to use the new pattern. The changes include new request/response types, generic Tool struct, and helper functions for consistent argument parsing.
Assessment
| @@ -0,0 +1,107 @@ | |||
| package mcpserver | |||
|
|
|||
| import ( | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🐛 Bug (High): Missing import for 'fmt' package. The file uses fmt.Errorf and fmt.Sprintf functions but doesn't import the fmt package.
| import ( | |
| import ( | |
| "context" | |
| "fmt" | |
| "dagger.io/dagger" | |
| "github.com/dagger/container-use/environment" | |
| "github.com/dagger/container-use/repository" | |
| "github.com/mark3labs/mcp-go/mcp" | |
| ) |
| import "github.com/mark3labs/mcp-go/mcp" | ||
|
|
||
| var ( | ||
| explainationArgument = mcp.WithString("explanation", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎨 Style (Low): Typo in variable name 'explainationArgument' should be 'explanationArgument'
| explainationArgument = mcp.WithString("explanation", | |
| explanationArgument = mcp.WithString("explanation", |
| func newRepositoryTool(name string, description string, args ...mcp.ToolOption) mcp.Tool { | ||
| opts := []mcp.ToolOption{ | ||
| mcp.WithDescription(description), | ||
| explainationArgument, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎨 Style (Low): Reference to typo variable name should be corrected to 'explanationArgument'
| explainationArgument, | |
| explanationArgument, |
| func newEnvironmentTool(name string, description string, args ...mcp.ToolOption) mcp.Tool { | ||
| opts := []mcp.ToolOption{ | ||
| mcp.WithDescription(description), | ||
| explainationArgument, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎨 Style (Low): Reference to typo variable name should be corrected to 'explanationArgument'
| explainationArgument, | |
| explanationArgument, |
| } | ||
|
|
||
| func parseBaseRequest(request mcp.CallToolRequest) (BaseRequest, error) { | ||
| explanation, err := request.RequireString("explanation") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🐛 Bug (Medium): The parseBaseRequest function tries to use RequireString for 'explanation' but based on the args.go file, explanation is not required. This should use GetString with a default value instead.
| explanation, err := request.RequireString("explanation") | |
| func parseBaseRequest(request mcp.CallToolRequest) (BaseRequest, error) { | |
| explanation := request.GetString("explanation", "") | |
| return BaseRequest{ | |
| Explanation: explanation, | |
| }, nil | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
⚡ Performance (Low): Inefficient slice allocation and appending in a loop. Pre-allocate the slice with known capacity to avoid multiple allocations.
| if portList, ok := request.GetArguments()["ports"].([]any); ok { | |
| req.Ports = make([]int, len(portList)) | |
| for i, port := range portList { | |
| req.Ports[i] = int(port.(float64)) | |
| } | |
| } |
| if portList, ok := request.GetArguments()["ports"].([]any); ok { | ||
| for _, port := range portList { | ||
| req.Ports = append(req.Ports, int(port.(float64))) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🐛 Bug (Medium): Potential panic from type assertion without checking. The type assertion port.(float64) could panic if the port value is not a float64.
| } | |
| for _, port := range portList { | |
| if portFloat, ok := port.(float64); ok { | |
| req.Ports = append(req.Ports, int(portFloat)) | |
| } else { | |
| return nil, fmt.Errorf("invalid port type: expected number, got %T", port) | |
| } | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔧 Maintainability (Low): Missing return value in the ToolResponse. The Data field is not populated for non-background command execution, which could be inconsistent with the generic pattern.
| return &ToolResponse[environment.EndpointMappings]{ | |
| Data: environment.EndpointMappings{}, // Empty mappings for non-background commands | |
| Message: fmt.Sprintf("%s\n\nAny changes to the container workdir (%s) have been committed and pushed to container-use/ remote", stdout, env.Config.Workdir), | |
| }, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔧 Maintainability (Medium): EnvironmentOpenTool is using the old Tool struct definition (Tool[Request, Response]) but hasn't been converted to the new generic pattern like EnvironmentCreateTool and EnvironmentFileListTool. This creates inconsistency in the codebase.
| // This tool should be converted to the new generic pattern like EnvironmentCreateTool | |
| // Consider creating a specific request struct and migrating to the new pattern |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎨 Style (Low): Inconsistent indentation in multi-line string. The description should maintain consistent indentation.
| `Creates a new development environment. | |
| The environment is the result of a the setups commands on top of the base image. | |
| Read carefully the instructions to understand the environment. | |
| DO NOT manually install toolchains inside the environment, instead explicitly call environment_update`, |
|
/cc @dagger |
|
/cc @dagger |
|
@dagger /review |
|
@dagger /review |
1 similar comment
|
@dagger /review |
|
@dagger /review |
|
@dagger /review |
|
@dagger review |
|
@dagger review |
|
@dagger review |
|
@dagger keep the typos |
|
@dagger you're wrong about the missing json import |
|
@dagger please review |
|
@dagger keep the typo |
|
@dagger review |
1 similar comment
|
@dagger review |
|
Test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤖 Code Review
Summary
This PR introduces a generic tool abstraction pattern for the MCP server, refactoring only two tools (EnvironmentCreateTool and EnvironmentFileListTool) to demonstrate the new architecture. The changes include creating type-safe request/response structures, separating concerns through different files (args.go, mcpserver.go), and introducing generic handler types.
Assessment
Positives:
- Good architectural direction: The generic
Tool[R Request, V Response]pattern provides better type safety than the previous untyped approach - Better separation of concerns: Moving common argument definitions to
args.goand shared utilities tomcpserver.goimproves code organization - Improved error handling: Using
fmt.Errorfwith%wverb for proper error wrapping
Issues Found:
- Critical bug: Variable shadowing of
jsonpackage import intools.go:112 - Missing import:
mcpserver.gousesfmt.Errorfbut doesn't importfmt - Typo: "explainationArgument" should be "explanationArgument" throughout
- Required field validation bug: Title field is marked as required but uses optional extraction
The refactoring pattern looks solid but needs these fixes before merging.
| return mcp.NewToolResultError(err.Error()), nil | ||
| } | ||
|
|
||
| json, err := json.Marshal(response.Data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🐛 Bug (High): Variable shadowing of json package import: The variable json on line 112 shadows the imported encoding/json package, which will cause compilation errors or unexpected behavior.
// Current problematic code:
json, err := json.Marshal(response.Data) // 'json' shadows the packageThis prevents access to the json.Marshal function and will likely cause a compilation error.
| json, err := json.Marshal(response.Data) | |
| data, err := json.Marshal(response.Data) | |
| if err != nil { | |
| return mcp.NewToolResultErrorFromErr("failed to marshal response", err), nil | |
| } | |
| body := string(data) |
| @@ -0,0 +1,107 @@ | |||
| package mcpserver | |||
|
|
|||
| import ( | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🐛 Bug (High): Missing import for fmt package: The file uses fmt.Errorf in multiple error handling locations (lines where functions return formatted errors), but the fmt package is not imported.
For example, the code uses fmt.Errorf("unable to open the repository: %w", err) pattern in the refactored tools, but fmt is not available in this file's scope.
| import ( | |
| import ( | |
| "context" | |
| "fmt" | |
| "dagger.io/dagger" | |
| "github.com/dagger/container-use/environment" | |
| "github.com/dagger/container-use/repository" | |
| "github.com/mark3labs/mcp-go/mcp" | |
| ) |
| import "github.com/mark3labs/mcp-go/mcp" | ||
|
|
||
| var ( | ||
| explainationArgument = mcp.WithString("explanation", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎨 Style (Medium): Typo in variable name: explainationArgument should be explanationArgument (missing 'n'). This typo is used consistently throughout the file on lines 6, 22, 33.
| explainationArgument = mcp.WithString("explanation", | |
| explanationArgument = mcp.WithString("explanation", | |
| mcp.Description("One sentence explanation for why this directory is being listed."), | |
| ) |
| return mcp.NewToolResultErrorFromErr("dagger client not found in context", nil), nil | ||
| return &EnvironmentCreateToolRequest{ | ||
| BaseRepositoryRequest: base, | ||
| Title: request.GetString("title", ""), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🐛 Bug (Medium): Required field validation inconsistency: The title field is defined as mcp.Required() in the tool definition (line 262), but the ParseRequest function uses request.GetString("title", "") which returns an empty string if the field is missing, rather than request.RequireString("title") which would return an error.
This means required field validation is bypassed, potentially allowing empty titles when they should be mandatory.
| Title: request.GetString("title", ""), | |
| title, err := request.RequireString("title") | |
| if err != nil { | |
| return nil, err | |
| } | |
| return &EnvironmentCreateToolRequest{ | |
| BaseRepositoryRequest: base, | |
| Title: title, | |
| }, nil |
Experiment. Only converted
EnvironmentCreateToolandEnvironmentFileListTool