Skip to content

jsonrpc: preserve integer request ID precision - #1375

Open
skosovsky wants to merge 1 commit into
modelcontextprotocol:mainfrom
skosovsky:fix/jsonrpc-integer-id-precision
Open

skosovsky wants to merge 1 commit into
modelcontextprotocol:mainfrom
skosovsky:fix/jsonrpc-integer-id-precision

Conversation

@skosovsky

@skosovsky skosovsky commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

JSON-RPC numeric ID 9007199254740993 is decoded as 9007199254740992. A successful server response therefore echoes a different ID, and two distinct incoming IDs can become the same internal map key. This reproduces on v1.8.0 and on main at 944ac19.

This change decodes envelope IDs through json.Number and converts them exactly to the existing int64 representation. Integral decimal/exponent forms remain supported using an exact rational conversion, with a magnitude check before allocating a rational. Fractional and out-of-int64-range wire IDs return an error instead of being truncated or overflowing.

Cancellation request IDs also decode as json.Number, so cancellation matches the preserved request ID. Metadata decoding remains unchanged. The exported MakeID function accepts json.Number; its existing float64 path is unchanged and cannot recover precision already lost by callers. Numeric CancelledParams.RequestID values decoded from JSON now have dynamic type json.Number, which is documented.

Tests cover request/response round trips above 2^53, signed int64 boundaries, integral decimal/exponent notation, invalid numeric IDs, and cancellation parameter decoding.

Validation:

  • go test ./... passed.
  • Targeted regression tests with -race passed.
  • go build ./... and git diff --check passed.
  • A separate raw HTTP reproduction against the patched SDK returned the exact ID 9007199254740993, and confirmed adjacent IDs remain distinct. That external harness is not included in this diff.
  • The new integer round-trip regression test failed on the unpatched checkout and passes with this change.
  • The separate MCP conformance suite was not run.

No module dependencies are added. The numeric ID representation remains limited to int64; arbitrary-precision IDs are outside this patch's scope.

Fixes #1376.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-11T13:59:12.501046Z 9174a3f PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@skosovsky

Copy link
Copy Markdown
Contributor Author

The Test, Conformance Tests, and CodeQL workflows currently report action_required, with no jobs started. This appears to be the approval gate for workflows from the fork. Could a maintainer approve these runs when convenient?

The local full Go test suite and targeted regression tests with the race detector passed, as noted in the PR description. Upstream CI, including the conformance suite, still needs to run.

guglielmo-san added a commit that referenced this pull request Oct 13, 2026
A peer cancellation is currently scheduled in a goroutine before looking
up its request. If the old invocation finishes and the read loop admits
a replacement with the same ID first, that lookup can cancel the
replacement.

Call `CancelCause` directly from `canceller.Preempt`, binding
cancellation to the active invocation before the read loop advances.
`CancelCause` captures the request under the existing connection lock
and cancels its context; it does not wait for handler completion or
response delivery. The peer's cancellation reason is preserved. No
public API, numeric-ID behavior, or response-suppression behavior
changes.

This PR changes only `mcp/transport.go`. The dedicated
cancellation-reuse test harness is retained downstream.

Validation on Go 1.27.1, darwin/arm64:

- After removing the dedicated test: `go test -race ./mcp -run
TestCancellation -count=1 -timeout=180s` and `git diff --check` passed.
- The unchanged production fix previously passed `go test ./...
-timeout=10m`, cancellation race tests, `go vet ./...`, and `go build
./...`.
- Separate protocol conformance scripts were not run.

Related: #1267 changes this same call to `CancelFromPeer`; if that lands
first, the invocation lookup should still happen before returning from
preemption. This fix is independent of #1375 and contains no numeric
decoding or transport hardening changes.

Fixes #1389.

---------

Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>

This branch has not been deployed

No deployments
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.

jsonrpc: numeric request IDs above 2^53 lose precision

1 participant