Skip to content

jsonrpc: normalize decimal and exponent integer IDs exactly - #1493

Closed
Shanmukh-l33t wants to merge 1 commit into
modelcontextprotocol:mainfrom
Shanmukh-l33t:fix/jsonrpc-id-precision
Closed

Shanmukh-l33t wants to merge 1 commit into
modelcontextprotocol:mainfrom
Shanmukh-l33t:fix/jsonrpc-id-precision

Conversation

@Shanmukh-l33t

Copy link
Copy Markdown

Problem

After #1401, integer-literal request IDs retain their precision, but equivalent decimal and exponent spellings still fall through MakeID's float64 fallback and lose precision above 2^53, as reported in #1404:

Input ID Expected Before this PR
9007199254740993 9007199254740993 9007199254740993
9007199254740993.0 9007199254740993 9007199254740992
90071992547409930e-1 9007199254740993 9007199254740992
-9007199254740993.0 -9007199254740993 -9007199254740992

This affects message decoding (a decoded Request.ID/Response.ID carries the wrong value, and a response echoes the wrong ID), and cancellation: an in-flight request with ID 9007199254740993 does not match notifications/cancelled whose requestId is spelled 9007199254740993.0, because both sides resolve their IDs through MakeID.

Solution

MakeID now normalizes a json.Number whose value is exactly an integer in the int64 range, whatever its spelling: the integer-literal fast path is kept, and decimal/exponent forms are parsed digit by digit into their exact value (9007199254740993.0, 90071992547409930e-1, 1.5e2, …). The exponent is handled arithmetically with a bound relative to the input length, so even absurd inputs like 0e1000000 cost no exponent-sized allocation and still resolve exactly. Only the numeric value is preserved, not the original spelling, so a response echoes the request ID's value exactly as JSON-RPC requires.

Per the scope proposed in #1404, values that are genuinely fractional (1.5) or out of the int64 range keep the existing float64 fallback, and the float64 input behavior is unchanged. This also restores ID equality for notifications/cancelled matching across spellings, since the canceller resolves both the request ID and requestId through MakeID.

Testing

  • New table-driven regression test TestIntegralNumberIDs in internal/jsonrpc2/wire_test.go covering the exact cases from jsonrpc: decimal and exponent integer IDs still lose precision after #1401 #1404 plus boundary (9223372036854775807.0, -9223372036854775808.0e0), huge-exponent (0e1000000), and fractional-fallback (1.5) cases. It round-trips each ID through DecodeMessage/EncodeMessage (both request and response forms) and checks MakeID(json.Number(...)) yields the same ID, which is the cancellation matching invariant. It fails before the fix with exactly the values from the issue table and passes after.
  • The parser was additionally validated out-of-band against an independent exact oracle on 600,055 generated inputs (random digit strings, decimal/exponent spellings, exponents up to ±10^25, int64 boundaries): zero mismatches.
  • gofmt -l . clean, go vet ./... clean, go test ./... passes (14 packages).

Fixes #1404

AI assistance: developed with an AI coding agent; the change and its reasoning are reviewed and owned by me.

Numeric request IDs that spell an integer in decimal or exponent form,
such as 9007199254740993.0 or 90071992547409930e-1, still went through
the float64 fallback in MakeID and lost precision above 2^53, even
though modelcontextprotocol#1401 made integer-literal IDs exact. The same loss affected
responses, which must echo the request ID exactly, and the matching of
notifications/cancelled, whose requestId also travels through MakeID.

MakeID now normalizes a json.Number whose value is an integer in the
int64 range exactly, whatever its spelling: the integer-literal fast
path stays, and other integral forms are parsed digit by digit into
their exact value, with no exponent-sized allocations. Values that are
genuinely fractional or out of the int64 range keep the existing
float64 fallback, and the float64 input behavior is unchanged.

Fixes modelcontextprotocol#1404
@github-actions github-actions Bot added the missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md) label Nov 3, 2026
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

This PR has been closed automatically. This repo only keeps pull requests open when they come from a maintainer, or from a contributor a maintainer has assigned to the linked issue, and you aren't currently assigned to #1404.

You're welcome to keep pushing commits here (just avoid force-pushing, since GitHub can't reopen a rewritten branch), but that on its own won't get the PR reviewed or the issue assigned. There's no need to open a new PR either way.

Maintainers: reopen, remove missing-issue-link, or add bypass-issue-check to override.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

jsonrpc: decimal and exponent integer IDs still lose precision after #1401

1 participant