Skip to content

Commit a8474c7

Browse files
committed
implement NewTypedToolHandlerFunc
1 parent 41f3ab2 commit a8474c7

File tree

4 files changed

+431
-2
lines changed

4 files changed

+431
-2
lines changed

examples/typed_tools/main.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/mark3labs/mcp-go/mcp"
8+
"github.com/mark3labs/mcp-go/server"
9+
)
10+
11+
// Define a struct for our typed arguments
12+
type GreetingArgs struct {
13+
Name string `json:"name"`
14+
Age int `json:"age"`
15+
IsVIP bool `json:"is_vip"`
16+
Languages []string `json:"languages"`
17+
Metadata struct {
18+
Location string `json:"location"`
19+
Timezone string `json:"timezone"`
20+
} `json:"metadata"`
21+
}
22+
23+
func main() {
24+
// Create a new MCP server
25+
s := server.NewMCPServer(
26+
"Typed Tools Demo 🚀",
27+
"1.0.0",
28+
server.WithToolCapabilities(false),
29+
)
30+
31+
// Add tool with complex schema
32+
tool := mcp.NewTool("greeting",
33+
mcp.WithDescription("Generate a personalized greeting"),
34+
mcp.WithString("name",
35+
mcp.Required(),
36+
mcp.Description("Name of the person to greet"),
37+
),
38+
mcp.WithNumber("age",
39+
mcp.Description("Age of the person"),
40+
mcp.Min(0),
41+
mcp.Max(150),
42+
),
43+
mcp.WithBoolean("is_vip",
44+
mcp.Description("Whether the person is a VIP"),
45+
mcp.DefaultBool(false),
46+
),
47+
mcp.WithArray("languages",
48+
mcp.Description("Languages the person speaks"),
49+
mcp.Items(map[string]any{"type": "string"}),
50+
),
51+
mcp.WithObject("metadata",
52+
mcp.Description("Additional information about the person"),
53+
mcp.Properties(map[string]any{
54+
"location": map[string]any{
55+
"type": "string",
56+
"description": "Current location",
57+
},
58+
"timezone": map[string]any{
59+
"type": "string",
60+
"description": "Timezone",
61+
},
62+
}),
63+
),
64+
)
65+
66+
// Add tool handler using the typed handler
67+
s.AddTool(tool, mcp.NewTypedToolHandler(typedGreetingHandler))
68+
69+
// Start the stdio server
70+
if err := server.ServeStdio(s); err != nil {
71+
fmt.Printf("Server error: %v\n", err)
72+
}
73+
}
74+
75+
// Our typed handler function that receives strongly-typed arguments
76+
func typedGreetingHandler(ctx context.Context, request mcp.CallToolRequest, args GreetingArgs) (*mcp.CallToolResult, error) {
77+
if args.Name == "" {
78+
return mcp.NewToolResultError("name is required"), nil
79+
}
80+
81+
// Build a personalized greeting based on the complex arguments
82+
greeting := fmt.Sprintf("Hello, %s!", args.Name)
83+
84+
if args.Age > 0 {
85+
greeting += fmt.Sprintf(" You are %d years old.", args.Age)
86+
}
87+
88+
if args.IsVIP {
89+
greeting += " Welcome back, valued VIP customer!"
90+
}
91+
92+
if len(args.Languages) > 0 {
93+
greeting += fmt.Sprintf(" You speak %d languages: %v.", len(args.Languages), args.Languages)
94+
}
95+
96+
if args.Metadata.Location != "" {
97+
greeting += fmt.Sprintf(" I see you're from %s.", args.Metadata.Location)
98+
99+
if args.Metadata.Timezone != "" {
100+
greeting += fmt.Sprintf(" Your timezone is %s.", args.Metadata.Timezone)
101+
}
102+
}
103+
104+
return mcp.NewToolResultText(greeting), nil
105+
}

mcp/tools.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ type CallToolResult struct {
4646
type CallToolRequest struct {
4747
Request
4848
Params struct {
49-
Name string `json:"name"`
50-
Arguments any `json:"arguments,omitempty"` // Can be map[string]any or any other type
49+
Name string `json:"name"`
50+
Arguments any `json:"arguments,omitempty"` // Can be map[string]any or any other type
5151
Meta *struct {
5252
// If specified, the caller is requesting out-of-band progress
5353
// notifications for this request (as represented by

mcp/typed_tools.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package mcp
2+
3+
import (
4+
"context"
5+
"fmt"
6+
)
7+
8+
// TypedToolHandlerFunc is a function that handles a tool call with typed arguments
9+
type TypedToolHandlerFunc[T any] func(ctx context.Context, request CallToolRequest, args T) (*CallToolResult, error)
10+
11+
// NewTypedToolHandler creates a ToolHandlerFunc that automatically binds arguments to a typed struct
12+
func NewTypedToolHandler[T any](handler TypedToolHandlerFunc[T]) func(ctx context.Context, request CallToolRequest) (*CallToolResult, error) {
13+
return func(ctx context.Context, request CallToolRequest) (*CallToolResult, error) {
14+
var args T
15+
if err := request.BindArguments(&args); err != nil {
16+
return NewToolResultError(fmt.Sprintf("failed to bind arguments: %v", err)), nil
17+
}
18+
return handler(ctx, request, args)
19+
}
20+
}

0 commit comments

Comments
 (0)