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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy