8000 fix(sqlite): preserve JSON number precision in tool results by feiiiiii5 · Pull Request #4431 · googleapis/mcp-toolbox · GitHub
[go: up one dir, main page]

Skip to content 8000

fix(sqlite): preserve JSON number precision in tool results - #4431

Open
feiiiiii5 wants to merge 1 commit into
googleapis:mainfrom
feiiiiii5:codex/sqlite-json-number-precision
Open

feiiiiii5 wants to merge 1 commit into
googleapis:mainfrom
feiiiiii5:codex/sqlite-json-number-precision

Conversation

@feiiiiii5
Copy link
Copy Markdown

Description

Fixes #4430. SQLite JSON results containing 64-bit IDs are rounded on the server: json_object('id', 9007199254740993) reaches the tool response as 9007199254740992.

Keep valid JSON as json.RawMessage instead of decoding numbers through any/float64. Both SQLite tools share this path; ordinary text retains its existing fallback. Related #3660 optimizes JSON detection and row serialization; it does not preserve numeric precision.

The real SQLite unit and public HTTP regression both fail on 06ea49d with the rounded ID and pass after the fix. SQLite source/tool unit packages, all four TestSQLiteExecuteSqlTool cases, source-package vet, scoped golangci-lint 2.15.0 and goimports formatting pass. Go 1.27.3; module files unchanged.

PR Checklist

  • Reviewed CONTRIBUTING.md.
  • Opened SQLite JSON tool results lose large integer precision #4430 before implementing the fix; a local diagnostic case first verified the report.
  • Reviewed the entire diff, including an independent agent review.
  • Relevant tests and linter pass.
  • Code coverage does not decrease (numeric coverage comparison not run locally).
  • Documentation assessed: this corrects existing JSON result behavior; no configuration or documentation change is needed.
  • Breaking-change assessment completed; no API signature changes.
Validation scope
go test -mod=readonly -p=2 ./internal/sources/sqlite -count=1
go test -mod=readonly -p=2 ./internal/tools/sqlite/... -count=1
go test -mod=readonly -p=2 ./tests/sqlite \
  -run '^TestSQLiteExecuteSqlTool$' -count=1 -v -timeout=10m
go vet -mod=readonly -p=2 ./internal/sources/sqlite
golangci-lint run --timeout 10m ./internal/sources/sqlite/... \
  ./internal/tools/sqlite/... ./tests/sqlite/...

The existing HTTP fixture was temporarily pointed at a free local port because 5000 was occupied; the port changes were restored. The regression exercises the real temporary database and tool endpoint. No external database or model service was used. The full repository integration matrix and coverage comparison were not run.

@feiiiiii5
feiiiiii5 requested a review from a team as a code owner November 6, 2026 11:25
@gemini-code-assist gemini-code-assist Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the SQLite source to preserve JSON number precision (such as large integers) by using json.RawMessage and json.Valid instead of unmarshaling to any, and adds corresponding unit and integration tests. The reviewer noted that using json.Valid on arbitrary strings can lead to unexpected type conversions for plain text columns (e.g., numeric strings or booleans) and suggested restricting the JSON detection heuristic to strings enclosed in braces or brackets.

Comment on lines 144 to 150
if jsonString, ok := val.(string); ok {
var unmarshaledData any
if json.Unmarshal([]byte(jsonString), &unmarshaledData) == nil {
row.Add(name, unmarshaledData)
jsonData := json.RawMessage(jsonString)
if json.Valid(jsonData) {
row.Add(name, jsonData)
continue
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using json.Valid on any string can lead to unexpected type conversions for plain text columns. For example, if a column contains a plain numeric string like "12345" (e.g., a zip code or phone number) or a boolean string like "true", json.Valid will return true. This causes the string to be treated as a JSON raw message and serialized as a JSON number or boolean in the final output, losing its original string type and potentially leading to bugs.

To prevent this and improve performance, we should restrict the JSON detection heuristic to strings that look like JSON objects or arrays (i.e., starting with { and ending with }, or starting with [ and ending with ]).

Suggested change
if jsonString, ok := val.(string); ok {
var unmarshaledData any
if json.Unmarshal([]byte(jsonString), &unmarshaledData) == nil {
row.Add(name, unmarshaledData)
jsonData := json.RawMessage(jsonString)
if json.Valid(jsonData) {
row.Add(name, jsonData)
continue
}
}
if jsonString, ok := val.(string); ok {
if len(jsonString) >= 2 && ((jsonString[0] == '{' && jsonString[len(jsonString)-1] == '}') || (jsonString[0] == '[' && jsonString[len(jsonString)-1] == ']')) {
jsonData := json.RawMessage(jsonString)
if json.Valid(jsonData) {
row.Add(name, jsonData)
continue
}
}
}

@feiiiiii5
Copy link
Copy Markdown
Author

The scalar detection is intentionally unchanged: base 06ea49d already runs json.Unmarshal into any, so text containing 12345 or true is already serialized as a JSON number or boolean. json.Valid accepts those same JSON values; this change preserves their original numeric tokens instead of decoding them through float64.

Restricting detection to objects and arrays would change the existing scalar behavior and belongs to the separate detection work discussed in #3660. I am keeping this fix focused on precision and retaining the current JSON detection contract.

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.

SQLite JSON tool results lose large integer precision

2 participants

0