From 8c9eb4abcbf867fba166e19e6fc8f232faaf1afc Mon Sep 17 00:00:00 2001 From: syf2211 Date: Fri, 3 Jul 2026 02:13:32 +0000 Subject: [PATCH] fix(issues): accept native integer types for issue_number parameters Extend RequiredInt/toInt coercion to handle int, int64, and json.Number in addition to float64 and numeric strings. Some MCP clients (including mcpcurl integer flags) pass native Go integer types that were previously rejected with 'expected number, got int'. Also add missing deprecated aliases for the issues consolidation rename: get_issue -> issue_read and update_issue -> issue_write. Fixes #2807 --- pkg/github/deprecated_tool_aliases.go | 5 +- pkg/github/issues_test.go | 14 ++++++ pkg/github/params.go | 67 ++++++++++++++++++--------- pkg/github/params_test.go | 14 ++++++ 4 files changed, 76 insertions(+), 24 deletions(-) diff --git a/pkg/github/deprecated_tool_aliases.go b/pkg/github/deprecated_tool_aliases.go index 4415731fbe..c3ac325613 100644 --- a/pkg/github/deprecated_tool_aliases.go +++ b/pkg/github/deprecated_tool_aliases.go @@ -10,7 +10,10 @@ package github // "get_issue": "issue_read", // "create_pr": "pull_request_create", var DeprecatedToolAliases = map[string]string{ - // Add entries as tools are renamed + // Issues tools consolidated (#1211) + "get_issue": "issue_read", + "update_issue": "issue_write", + // Actions tools consolidated "list_workflows": "actions_list", "list_workflow_runs": "actions_list", diff --git a/pkg/github/issues_test.go b/pkg/github/issues_test.go index 2dea639f8a..ce855742c7 100644 --- a/pkg/github/issues_test.go +++ b/pkg/github/issues_test.go @@ -606,6 +606,20 @@ func Test_AddIssueComment(t *testing.T) { expectError: false, expectedComment: mockComment, }, + { + name: "successful comment creation with int issue_number", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + PostReposIssuesCommentsByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusCreated, mockComment), + }), + requestArgs: map[string]any{ + "owner": "owner", + "repo": "repo", + "issue_number": int(42), + "body": "This is a test comment", + }, + expectError: false, + expectedComment: mockComment, + }, { name: "comment creation fails", mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ diff --git a/pkg/github/params.go b/pkg/github/params.go index a6b43375ef..20302ed268 100644 --- a/pkg/github/params.go +++ b/pkg/github/params.go @@ -1,6 +1,7 @@ package github import ( + "encoding/json" "errors" "fmt" "math" @@ -40,23 +41,52 @@ func isAcceptedError(err error) bool { return errors.As(err, &acceptedError) } -// toInt converts a value to int, handling both float64 and string representations. -// Some MCP clients send numeric values as strings. It rejects NaN, ±Inf, -// fractional values, and values outside the int range. -func toInt(val any) (int, error) { - var f float64 +// numericToFloat64 normalizes numeric tool arguments to float64. +// MCP clients may send JSON numbers as float64 (default json.Unmarshal), +// native integer types (e.g. mcpcurl integer flags), or strings. +func numericToFloat64(val any) (float64, error) { switch v := val.(type) { case float64: - f = v + return v, nil + case float32: + return float64(v), nil + case int: + return float64(v), nil + case int32: + return float64(v), nil + case int64: + return float64(v), nil + case uint: + return float64(v), nil + case uint32: + return float64(v), nil + case uint64: + return float64(v), nil case string: - var err error - f, err = strconv.ParseFloat(v, 64) + f, err := strconv.ParseFloat(v, 64) + if err != nil { + return 0, fmt.Errorf("invalid numeric value: %s", v) + } + return f, nil + case json.Number: + f, err := v.Float64() if err != nil { return 0, fmt.Errorf("invalid numeric value: %s", v) } + return f, nil default: return 0, fmt.Errorf("expected number, got %T", val) } +} + +// toInt converts a value to int, handling float64, integer, and string representations. +// Some MCP clients send numeric values as strings or native integer types. It rejects +// NaN, ±Inf, fractional values, and values outside the int range. +func toInt(val any) (int, error) { + f, err := numericToFloat64(val) + if err != nil { + return 0, err + } if math.IsNaN(f) || math.IsInf(f, 0) { return 0, fmt.Errorf("non-finite numeric value") } @@ -69,22 +99,13 @@ func toInt(val any) (int, error) { return int(f), nil } -// toInt64 converts a value to int64, handling both float64 and string representations. -// Some MCP clients send numeric values as strings. It rejects NaN, ±Inf, -// fractional values, and values that lose precision in the float64→int64 conversion. +// toInt64 converts a value to int64, handling float64, integer, and string representations. +// Some MCP clients send numeric values as strings or native integer types. It rejects +// NaN, ±Inf, fractional values, and values that lose precision in the float64→int64 conversion. func toInt64(val any) (int64, error) { - var f float64 - switch v := val.(type) { - case float64: - f = v - case string: - var err error - f, err = strconv.ParseFloat(v, 64) - if err != nil { - return 0, fmt.Errorf("invalid numeric value: %s", v) - } - default: - return 0, fmt.Errorf("expected number, got %T", val) + f, err := numericToFloat64(val) + if err != nil { + return 0, err } if math.IsNaN(f) || math.IsInf(f, 0) { return 0, fmt.Errorf("non-finite numeric value") diff --git a/pkg/github/params_test.go b/pkg/github/params_test.go index b00efeb10c..0cff0ee4ec 100644 --- a/pkg/github/params_test.go +++ b/pkg/github/params_test.go @@ -171,6 +171,20 @@ func Test_RequiredInt(t *testing.T) { expected: 42, expectError: false, }, + { + name: "valid int parameter", + params: map[string]any{"count": int(42)}, + paramName: "count", + expected: 42, + expectError: false, + }, + { + name: "valid int64 parameter", + params: map[string]any{"count": int64(42)}, + paramName: "count", + expected: 42, + expectError: false, + }, { name: "missing parameter", params: map[string]any{},