镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

mcp: preserve structured content number precision - #1239

Merged
guglielmo-san merged 3 commits into
modelcontextprotocol:mainfrom
CaliLuke:codex/issue-1236-structured-content-numbers
Sep 16, 2026
Merged

guglielmo-san merged 3 commits into
modelcontextprotocol:mainfrom
CaliLuke:codex/issue-1236-structured-content-numbers

Conversation

@CaliLuke

@CaliLuke CaliLuke commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

CallToolResult currently decodes structuredContent through an any field, which converts JSON numbers to float64 and silently rounds integers outside the IEEE-754 safe range.

This change decodes structuredContent separately with UseNumber, preserving wire numbers as json.Number while retaining the existing decoded object, array, and primitive shapes. The field documentation now describes that representation. Users who depend on the previous float64 behavior can temporarily restore it with MCPGODEBUG=structuredcontentfloat64=1.

An end-to-end in-memory client/server regression covers 9007199254740993 from a low-level Server.AddTool result. Additional coverage verifies the compatibility option restores the legacy rounded float64 value.

Verification:

  • gofmt -l .
  • go test ./...
  • go vet ./...

Fixes #1236

Comment thread mcp/protocol.go
@guglielmo-san

Copy link
Copy Markdown
Contributor

Thank you for the contribution!

@guglielmo-san
guglielmo-san merged commit 3785c50 into modelcontextprotocol:main Sep 16, 2026
10 checks passed
yehia-khalil added a commit to yehia-khalil/go-sdk that referenced this pull request Sep 26, 2026
modelcontextprotocol#1239 fixed the CLIENT side of this: CallToolResult.StructuredContent is
typed `any`, so a Go client decoded wire numbers into float64. This is the
SERVER side, which that change did not reach — the bytes are already wrong
before they are sent, so every client is affected, including clients that
are not written in Go and never touch CallToolResult at all.

applySchema decodes a tool's arguments and its result into `any` and
re-marshals whenever defaults may have been applied — which is every
object-rooted schema, so in practice always. The decoder had no
UseNumber(), so every JSON number became a float64 and the re-marshal
wrote the lossy value back out.

An integer that is exact in JSON and exact in int64 but not in float64
therefore arrived correct and left wrong, silently:

    in:  {"id":9007199254740993}
    out: {"id":9007199254740992}

Snowflake ids, Discord/Twitter-style ids and any BIGINT primary key
allocated above 2^53 are all in range, on both the argument path
(server.go:360) and the result path (server.go:422).

UseNumber() alone does not fix it: json.Number is a string type, and
jsonschema reports it as `has type "string", want "integer"`. So the
decoded tree is narrowed first — json.Number becomes int64 when it is an
integer that fits and float64 otherwise. Both validate correctly against
"integer" and "number", and both re-marshal to the text they were decoded
from.

This reuses modelcontextprotocol#1239's internal/json.UnmarshalUseNumber rather than adding a
second decoder helper. That function also runs checkMaxDepth, which an
earlier revision of this change did not — reusing it closes that gap as
well as avoiding the duplicate.

A JSON integer outside int64's range still falls back to float64; that is
documented on narrowNumbers. Representing it exactly would need a big.Int,
which jsonschema does not accept.

Tests cover the argument path, the result path, nesting inside arrays and
objects (a database row set is an array of arrays), and that a float is
still a float afterwards.

Verification:
- gofmt -l .
- go build ./...
- go vet ./...
- go test ./...   (14 packages, 0 failures)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mcp: CallToolResult structuredContent loses large integer precision

2 participants