fix: do not omitempty ReadOnlyHint in ToolAnnotations (#908)

`ReadOnlyHint` is a `bool` with omitempty, so the zero value (false) is
indistinguishable from unset, and **drops out** of marshaled JSON.

The current behavior causes an issue with the OpenAI MCP app submission
because they require explicit hints.

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 <noreply@anthropic.com>
Co-authored-by: Maciej Kisiel <mkisiel@google.com>
Co-authored-by: guglielmoc <guglielmoc@google.com>
This commit is contained in:
Pavel Bazin 2026-05-25 10:52:15 -03:00 • committed by GitHub
commit c044cdb734
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 107 additions and 2 deletions

View file

@ -27,6 +27,12 @@ Options listed below were added and will be removed in the 1.9.0 version of the
Params), restoring the previous behavior. The default behavior was changed to
align with SEP-2164 and the JSON-RPC specification.
- `hintomitempty` added. If set to `1`, `ToolAnnotations` JSON marshaling
will omit `ReadOnlyHint` and `IdempotentHint` when their value is `false`,
restoring the previous behavior. The default behavior was changed to always
serialize these fields, since their Go types are bare `bool` (not `*bool`)
and omitting `false` made it indistinguishable from unset.
- `allowsessionsinstateless` added. If set to `1`, stateless streamable HTTP
servers will read the `Mcp-Session-Id` request header (or generate one via
`GetSessionID`), set it on response headers, and accept `DELETE` requests,

View file

@ -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`.