Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions internal/mcp_impl/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import (
"github.com/mark3labs/mcp-go/server"
)

// maxExactJSONInt is the largest integer that float64 can represent
// without loss (2^53 - 1). JSON-decoded amounts past this magnitude have
// already lost precision before reaching the handler, so we reject them.
const maxExactJSONInt = float64(9_007_199_254_740_991)

func parseBalancesJson(balancesRaw any) (interpreter.Balances, *mcp.CallToolResult) {
balances, ok := balancesRaw.(map[string]any)
if !ok {
Expand All @@ -33,11 +38,20 @@ func parseBalancesJson(balancesRaw any) (interpreter.Balances, *mcp.CallToolResu
for asset, amountRaw := range assets {
amount, ok := amountRaw.(float64)
if !ok {
return interpreter.Balances{}, mcp.NewToolResultError(fmt.Sprintf("Expected float for amount: %v", amountRaw))
return interpreter.Balances{}, mcp.NewToolResultError(fmt.Sprintf("Expected number for amount on %s/%s, got: <%#v>", account, asset, amountRaw))
}

// JSON numbers arrive here as float64. Reject anything that
// cannot be losslessly represented as an integer in float64
// precision: fractional values and magnitudes past 2^53 - 1.
// Silent truncation / rounding on a balance is not acceptable
// for a financial DSL — the caller should switch to a safer
// encoding when they need values outside the safe range.
if amount < -maxExactJSONInt || amount > maxExactJSONInt || amount != float64(int64(amount)) {
return interpreter.Balances{}, mcp.NewToolResultError(fmt.Sprintf("amount for %s/%s must be an exact integer in [-(2^53-1), 2^53-1], got: %v", account, asset, amount))
}

n, _ := big.NewFloat(amount).Int(new(big.Int))
iBalances[account][asset] = n
iBalances[account][asset] = big.NewInt(int64(amount))
}
}
return iBalances, nil
Expand Down