Skip to content
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