Skip to content

refactor: make CallToolRequest.Arguments more flexible (Breaking Change) #287

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

Merged
merged 13 commits into from
May 17, 2025
Merged
32 changes: 23 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,21 @@ func main() {

// Add the calculator handler
s.AddTool(calculatorTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
op := request.Params.Arguments["operation"].(string)
x := request.Params.Arguments["x"].(float64)
y := request.Params.Arguments["y"].(float64)
// Using helper functions for type-safe argument access
op, err := request.RequireString("operation")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}

x, err := request.RequireFloat("x")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}

y, err := request.RequireFloat("y")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}

var result float64
switch op {
Expand Down Expand Up @@ -312,9 +324,10 @@ calculatorTool := mcp.NewTool("calculate",
)

s.AddTool(calculatorTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
op := request.Params.Arguments["operation"].(string)
x := request.Params.Arguments["x"].(float64)
y := request.Params.Arguments["y"].(float64)
args := request.GetArguments()
op := args["operation"].(string)
x := args["x"].(float64)
y := args["y"].(float64)

var result float64
switch op {
Expand Down Expand Up @@ -355,10 +368,11 @@ httpTool := mcp.NewTool("http_request",
)

s.AddTool(httpTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
method := request.Params.Arguments["method"].(string)
url := request.Params.Arguments["url"].(string)
args := request.GetArguments()
method := args["method"].(string)
url := args["url"].(string)
body := ""
if b, ok := request.Params.Arguments["body"].(string); ok {
if b, ok := args["body"].(string); ok {
body = b
}

Expand Down
2 changes: 1 addition & 1 deletion client/inprocess_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func TestInProcessMCPClient(t *testing.T) {
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: "Input parameter: " + request.Params.Arguments["parameter-1"].(string),
Text: "Input parameter: " + request.GetArguments()["parameter-1"].(string),
},
mcp.AudioContent{
Type: "audio",
Expand Down
2 changes: 1 addition & 1 deletion client/sse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func TestSSEMCPClient(t *testing.T) {
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: "Input parameter: " + request.Params.Arguments["parameter-1"].(string),
Text: "Input parameter: " + request.GetArguments()["parameter-1"].(string),
},
},
}, nil
Expand Down
2 changes: 1 addition & 1 deletion examples/custom_context/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func handleMakeAuthenticatedRequestTool(
ctx context.Context,
request mcp.CallToolRequest,
) (*mcp.CallToolResult, error) {
message, ok := request.Params.Arguments["message"].(string)
message, ok := request.GetArguments()["message"].(string)
if !ok {
return nil, fmt.Errorf("missing message")
}
Expand Down
2 changes: 1 addition & 1 deletion examples/dynamic_path/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func main() {

// Add a trivial tool for demonstration
mcpServer.AddTool(mcp.NewTool("echo"), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return mcp.NewToolResultText(fmt.Sprintf("Echo: %v", req.Params.Arguments["message"])), nil
return mcp.NewToolResultText(fmt.Sprintf("Echo: %v", req.GetArguments()["message"])), nil
})

// Use a dynamic base path based on a path parameter (Go 1.22+)
Expand Down
6 changes: 3 additions & 3 deletions examples/everything/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ func handleEchoTool(
ctx context.Context,
request mcp.CallToolRequest,
) (*mcp.CallToolResult, error) {
arguments := request.Params.Arguments
arguments := request.GetArguments()
message, ok := arguments["message"].(string)
if !ok {
return nil, fmt.Errorf("invalid message argument")
Expand All @@ -331,7 +331,7 @@ func handleAddTool(
ctx context.Context,
request mcp.CallToolRequest,
) (*mcp.CallToolResult, error) {
arguments := request.Params.Arguments
arguments := request.GetArguments()
a, ok1 := arguments["a"].(float64)
b, ok2 := arguments["b"].(float64)
if !ok1 || !ok2 {
Expand Down Expand Up @@ -382,7 +382,7 @@ func handleLongRunningOperationTool(
ctx context.Context,
request mcp.CallToolRequest,
) (*mcp.CallToolResult, error) {
arguments := request.Params.Arguments
arguments := request.GetArguments()
progressToken := request.Params.Meta.ProgressToken
duration, _ := arguments["duration"].(float64)
steps, _ := arguments["steps"].(float64)
Expand Down
105 changes: 105 additions & 0 deletions examples/typed_tools/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package main

import (
"context"
"fmt"

"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)

// Define a struct for our typed arguments
type GreetingArgs struct {
Name string `json:"name"`
Age int `json:"age"`
IsVIP bool `json:"is_vip"`
Languages []string `json:"languages"`
Metadata struct {
Location string `json:"location"`
Timezone string `json:"timezone"`
} `json:"metadata"`
}

func main() {
// Create a new MCP server
s := server.NewMCPServer(
"Typed Tools Demo 🚀",
"1.0.0",
server.WithToolCapabilities(false),
)

// Add tool with complex schema
tool := mcp.NewTool("greeting",
mcp.WithDescription("Generate a personalized greeting"),
mcp.WithString("name",
mcp.Required(),
mcp.Description("Name of the person to greet"),
),
mcp.WithNumber("age",
mcp.Description("Age of the person"),
mcp.Min(0),
mcp.Max(150),
),
mcp.WithBoolean("is_vip",
mcp.Description("Whether the person is a VIP"),
mcp.DefaultBool(false),
),
mcp.WithArray("languages",
mcp.Description("Languages the person speaks"),
mcp.Items(map[string]any{"type": "string"}),
),
mcp.WithObject("metadata",
mcp.Description("Additional information about the person"),
mcp.Properties(map[string]any{
"location": map[string]any{
"type": "string",
"description": "Current location",
},
"timezone": map[string]any{
"type": "string",
"description": "Timezone",
},
}),
),
)

// Add tool handler using the typed handler
s.AddTool(tool, mcp.NewTypedToolHandler(typedGreetingHandler))

// Start the stdio server
if err := server.ServeStdio(s); err != nil {
fmt.Printf("Server error: %v\n", err)
}
}

// Our typed handler function that receives strongly-typed arguments
func typedGreetingHandler(ctx context.Context, request mcp.CallToolRequest, args GreetingArgs) (*mcp.CallToolResult, error) {
if args.Name == "" {
return mcp.NewToolResultError("name is required"), nil
}

// Build a personalized greeting based on the complex arguments
greeting := fmt.Sprintf("Hello, %s!", args.Name)

if args.Age > 0 {
greeting += fmt.Sprintf(" You are %d years old.", args.Age)
}

if args.IsVIP {
greeting += " Welcome back, valued VIP customer!"
}

if len(args.Languages) > 0 {
greeting += fmt.Sprintf(" You speak %d languages: %v.", len(args.Languages), args.Languages)
}

if args.Metadata.Location != "" {
greeting += fmt.Sprintf(" I see you're from %s.", args.Metadata.Location)

if args.Metadata.Timezone != "" {
greeting += fmt.Sprintf(" Your timezone is %s.", args.Metadata.Timezone)
}
}

return mcp.NewToolResultText(greeting), nil
}
Loading