From 6f5b970824ad2cfcfed0dc4b3936a667eccd5936 Mon Sep 17 00:00:00 2001 From: Pavel Bazin Date: Fri, 24 Apr 2026 13:06:03 -0300 Subject: [PATCH 1/3] mcp: do not omitempty ReadOnlyHint in ToolAnnotations ReadOnlyHint is a bare bool with omitempty, so the zero value (false) is indistinguishable from unset and drops out of marshaled JSON. Consumers that explicitly set ReadOnlyHint: false on write tools lose the field on the wire. Removing omitempty ensures false is always serialized, which matches the MCP spec default. Co-Authored-By: Claude Opus 4.6 --- mcp/protocol.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcp/protocol.go b/mcp/protocol.go index 1646788a..39be432f 100644 --- a/mcp/protocol.go +++ b/mcp/protocol.go @@ -1378,7 +1378,7 @@ type ToolAnnotations struct { // If true, the tool does not modify its environment. // // Default: false - ReadOnlyHint bool `json:"readOnlyHint,omitempty"` + ReadOnlyHint bool `json:"readOnlyHint"` // A human-readable title for the tool. Title string `json:"title,omitempty"` } From 7cd2e561ec8cdfb202394b929c6100db85460307 Mon Sep 17 00:00:00 2001 From: Pavel Bazin Date: Thu, 30 Apr 2026 15:10:09 -0300 Subject: [PATCH 2/3] fix: remove omit empty from IdempotentHint --- mcp/protocol.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcp/protocol.go b/mcp/protocol.go index 39be432f..c50ea29f 100644 --- a/mcp/protocol.go +++ b/mcp/protocol.go @@ -1368,7 +1368,7 @@ type ToolAnnotations struct { // (This property is meaningful only when ReadOnlyHint == false.) // // Default: false - IdempotentHint bool `json:"idempotentHint,omitempty"` + IdempotentHint bool `json:"idempotentHint"` // If true, this tool may interact with an "open world" of external entities. If // false, the tool's domain of interaction is closed. For example, the world of // a web search tool is open, whereas that of a memory tool is not. From 92701776762cb176393b971585f85aa4d105d7dc Mon Sep 17 00:00:00 2001 From: Pavel Bazin Date: Thu, 30 Apr 2026 15:15:52 -0300 Subject: [PATCH 3/3] doc: add *bool vs bool case to rough edges --- docs/rough_edges.md | 5 +++++ internal/docs/rough_edges.src.md | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/docs/rough_edges.md b/docs/rough_edges.md index 7ee18b1a..c1a618df 100644 --- a/docs/rough_edges.md +++ b/docs/rough_edges.md @@ -63,3 +63,8 @@ v2. - `StreamableHTTPOptions.CrossOriginProtection` should not have been part of the SDK API. Cross-origin protection is a general HTTP concern, not specific to MCP, and can be applied as standard HTTP middleware. + +- `ToolAnnotations` (`mcp/protocol.go`) should have all fields typed as `*bool` + for full control to define what is being sent over the wire. Different + MCP clients have different requirements, and some of them require all fields + to be explicitly set to either `true` or `false`. diff --git a/internal/docs/rough_edges.src.md b/internal/docs/rough_edges.src.md index d573e141..90751f74 100644 --- a/internal/docs/rough_edges.src.md +++ b/internal/docs/rough_edges.src.md @@ -62,3 +62,8 @@ v2. - `StreamableHTTPOptions.CrossOriginProtection` should not have been part of the SDK API. Cross-origin protection is a general HTTP concern, not specific to MCP, and can be applied as standard HTTP middleware. + +- `ToolAnnotations` (`mcp/protocol.go`) should have all fields typed as `*bool` + for full control to define what is being sent over the wire. Different + MCP clients have different requirements, and some of them require all fields + to be explicitly set to either `true` or `false`. \ No newline at end of file