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{},