Skip to content

Commit eeb7070

Browse files
authored
fix: properly marshal ToolAnnotations with false values (#260)
1 parent e1f1b47 commit eeb7070

File tree

5 files changed

+78
-35
lines changed

5 files changed

+78
-35
lines changed

client/inprocess_test.go

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,11 @@ func TestInProcessMCPClient(t *testing.T) {
2222
"test-tool",
2323
mcp.WithDescription("Test tool"),
2424
mcp.WithString("parameter-1", mcp.Description("A string tool parameter")),
25-
mcp.WithToolAnnotation(mcp.ToolAnnotation{
26-
Title: "Test Tool Annotation Title",
27-
ReadOnlyHint: true,
28-
DestructiveHint: false,
29-
IdempotentHint: true,
30-
OpenWorldHint: false,
31-
}),
25+
mcp.WithTitleAnnotation("Test Tool Annotation Title"),
26+
mcp.WithReadOnlyHintAnnotation(true),
27+
mcp.WithDestructiveHintAnnotation(false),
28+
mcp.WithIdempotentHintAnnotation(true),
29+
mcp.WithOpenWorldHintAnnotation(false),
3230
), func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
3331
return &mcp.CallToolResult{
3432
Content: []mcp.Content{
@@ -143,10 +141,10 @@ func TestInProcessMCPClient(t *testing.T) {
143141
}
144142
testToolAnnotations := (*toolListResult).Tools[0].Annotations
145143
if testToolAnnotations.Title != "Test Tool Annotation Title" ||
146-
testToolAnnotations.ReadOnlyHint != true ||
147-
testToolAnnotations.DestructiveHint != false ||
148-
testToolAnnotations.IdempotentHint != true ||
149-
testToolAnnotations.OpenWorldHint != false {
144+
*testToolAnnotations.ReadOnlyHint != true ||
145+
*testToolAnnotations.DestructiveHint != false ||
146+
*testToolAnnotations.IdempotentHint != true ||
147+
*testToolAnnotations.OpenWorldHint != false {
150148
t.Errorf("The annotations of the tools are invalid")
151149
}
152150
})

client/sse_test.go

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ package client
22

33
import (
44
"context"
5-
"github.com/mark3labs/mcp-go/client/transport"
65
"testing"
76
"time"
87

8+
"github.com/mark3labs/mcp-go/client/transport"
9+
910
"github.com/mark3labs/mcp-go/mcp"
1011
"github.com/mark3labs/mcp-go/server"
1112
)
@@ -25,13 +26,11 @@ func TestSSEMCPClient(t *testing.T) {
2526
"test-tool",
2627
mcp.WithDescription("Test tool"),
2728
mcp.WithString("parameter-1", mcp.Description("A string tool parameter")),
28-
mcp.WithToolAnnotation(mcp.ToolAnnotation{
29-
Title: "Test Tool Annotation Title",
30-
ReadOnlyHint: true,
31-
DestructiveHint: false,
32-
IdempotentHint: true,
33-
OpenWorldHint: false,
34-
}),
29+
mcp.WithTitleAnnotation("Test Tool Annotation Title"),
30+
mcp.WithReadOnlyHintAnnotation(true),
31+
mcp.WithDestructiveHintAnnotation(false),
32+
mcp.WithIdempotentHintAnnotation(true),
33+
mcp.WithOpenWorldHintAnnotation(false),
3534
), func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
3635
return &mcp.CallToolResult{
3736
Content: []mcp.Content{
@@ -111,10 +110,10 @@ func TestSSEMCPClient(t *testing.T) {
111110
}
112111
testToolAnnotations := (*toolListResult).Tools[0].Annotations
113112
if testToolAnnotations.Title != "Test Tool Annotation Title" ||
114-
testToolAnnotations.ReadOnlyHint != true ||
115-
testToolAnnotations.DestructiveHint != false ||
116-
testToolAnnotations.IdempotentHint != true ||
117-
testToolAnnotations.OpenWorldHint != false {
113+
*testToolAnnotations.ReadOnlyHint != true ||
114+
*testToolAnnotations.DestructiveHint != false ||
115+
*testToolAnnotations.IdempotentHint != true ||
116+
*testToolAnnotations.OpenWorldHint != false {
118117
t.Errorf("The annotations of the tools are invalid")
119118
}
120119
})

mcp/tools.go

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,13 @@ type ToolAnnotation struct {
139139
// Human-readable title for the tool
140140
Title string `json:"title,omitempty"`
141141
// If true, the tool does not modify its environment
142-
ReadOnlyHint bool `json:"readOnlyHint,omitempty"`
142+
ReadOnlyHint *bool `json:"readOnlyHint,omitempty"`
143143
// If true, the tool may perform destructive updates
144-
DestructiveHint bool `json:"destructiveHint,omitempty"`
144+
DestructiveHint *bool `json:"destructiveHint,omitempty"`
145145
// If true, repeated calls with same args have no additional effect
146-
IdempotentHint bool `json:"idempotentHint,omitempty"`
146+
IdempotentHint *bool `json:"idempotentHint,omitempty"`
147147
// If true, tool interacts with external entities
148-
OpenWorldHint bool `json:"openWorldHint,omitempty"`
148+
OpenWorldHint *bool `json:"openWorldHint,omitempty"`
149149
}
150150

151151
// ToolOption is a function that configures a Tool.
@@ -173,10 +173,10 @@ func NewTool(name string, opts ...ToolOption) Tool {
173173
},
174174
Annotations: ToolAnnotation{
175175
Title: "",
176-
ReadOnlyHint: false,
177-
DestructiveHint: true,
178-
IdempotentHint: false,
179-
OpenWorldHint: true,
176+
ReadOnlyHint: ToBoolPtr(false),
177+
DestructiveHint: ToBoolPtr(true),
178+
IdempotentHint: ToBoolPtr(false),
179+
OpenWorldHint: ToBoolPtr(true),
180180
},
181181
}
182182

@@ -212,12 +212,53 @@ func WithDescription(description string) ToolOption {
212212
}
213213
}
214214

215+
// WithToolAnnotation adds optional hints about the Tool.
215216
func WithToolAnnotation(annotation ToolAnnotation) ToolOption {
216217
return func(t *Tool) {
217218
t.Annotations = annotation
218219
}
219220
}
220221

222+
// WithTitleAnnotation sets the Title field of the Tool's Annotations.
223+
// It provides a human-readable title for the tool.
224+
func WithTitleAnnotation(title string) ToolOption {
225+
return func(t *Tool) {
226+
t.Annotations.Title = title
227+
}
228+
}
229+
230+
// WithReadOnlyHintAnnotation sets the ReadOnlyHint field of the Tool's Annotations.
231+
// If true, it indicates the tool does not modify its environment.
232+
func WithReadOnlyHintAnnotation(value bool) ToolOption {
233+
return func(t *Tool) {
234+
t.Annotations.ReadOnlyHint = &value
235+
}
236+
}
237+
238+
// WithDestructiveHintAnnotation sets the DestructiveHint field of the Tool's Annotations.
239+
// If true, it indicates the tool may perform destructive updates.
240+
func WithDestructiveHintAnnotation(value bool) ToolOption {
241+
return func(t *Tool) {
242+
t.Annotations.DestructiveHint = &value
243+
}
244+
}
245+
246+
// WithIdempotentHintAnnotation sets the IdempotentHint field of the Tool's Annotations.
247+
// If true, it indicates repeated calls with the same arguments have no additional effect.
248+
func WithIdempotentHintAnnotation(value bool) ToolOption {
249+
return func(t *Tool) {
250+
t.Annotations.IdempotentHint = &value
251+
}
252+
}
253+
254+
// WithOpenWorldHintAnnotation sets the OpenWorldHint field of the Tool's Annotations.
255+
// If true, it indicates the tool interacts with external entities.
256+
func WithOpenWorldHintAnnotation(value bool) ToolOption {
257+
return func(t *Tool) {
258+
t.Annotations.OpenWorldHint = &value
259+
}
260+
}
261+
221262
//
222263
// Common Property Options
223264
//

mcp/utils.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,3 +775,8 @@ func ParseStringMap(request CallToolRequest, key string, defaultValue map[string
775775
v := ParseArgument(request, key, defaultValue)
776776
return cast.ToStringMap(v)
777777
}
778+
779+
// ToBoolPtr returns a pointer to the given boolean value
780+
func ToBoolPtr(b bool) *bool {
781+
return &b
782+
}

server/server_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -900,10 +900,10 @@ func TestMCPServer_HandleUndefinedHandlers(t *testing.T) {
900900
},
901901
Annotations: mcp.ToolAnnotation{
902902
Title: "test-tool",
903-
ReadOnlyHint: true,
904-
DestructiveHint: false,
905-
IdempotentHint: false,
906-
OpenWorldHint: false,
903+
ReadOnlyHint: mcp.ToBoolPtr(true),
904+
DestructiveHint: mcp.ToBoolPtr(false),
905+
IdempotentHint: mcp.ToBoolPtr(false),
906+
OpenWorldHint: mcp.ToBoolPtr(false),
907907
},
908908
}, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
909909
return &mcp.CallToolResult{}, nil

0 commit comments

Comments
 (0)