Skip to content

Commit ec67616

Browse files
authored
Add new RequiredIntArrayParam helper
1 parent 8e1e341 commit ec67616

File tree

3 files changed

+149
-6
lines changed

3 files changed

+149
-6
lines changed

pkg/github/issues.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,16 +1480,11 @@ func FindClosingPullRequests(getGQLClient GetGQLClientFn, t translations.Transla
14801480
if err != nil {
14811481
return mcp.NewToolResultError(fmt.Sprintf("repo parameter error: %s", err.Error())), nil
14821482
}
1483-
issueNumbers, err := OptionalIntArrayParam(request, "issue_numbers")
1483+
issueNumbers, err := RequiredIntArrayParam(request, "issue_numbers")
14841484
if err != nil {
14851485
return mcp.NewToolResultError(fmt.Sprintf("issue_numbers parameter error: %s", err.Error())), nil
14861486
}
14871487

1488-
// Validate that issue_numbers is not empty
1489-
if len(issueNumbers) == 0 {
1490-
return mcp.NewToolResultError("issue_numbers parameter is required and cannot be empty - provide at least one issue number"), nil
1491-
}
1492-
14931488
// Get GraphQL client
14941489
client, err := getGQLClient(ctx)
14951490
if err != nil {

pkg/github/server.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,50 @@ func RequiredInt(r mcp.CallToolRequest, p string) (int, error) {
9999
return int(v), nil
100100
}
101101

102+
// RequiredIntArrayParam is a helper function that can be used to fetch a required integer array parameter from the request.
103+
// It does the following checks:
104+
// 1. Checks if the parameter is present in the request
105+
// 2. Checks if the parameter is an array and each element can be converted to int
106+
// 3. Checks if the array is not empty
107+
func RequiredIntArrayParam(r mcp.CallToolRequest, p string) ([]int, error) {
108+
// Check if the parameter is present in the request
109+
if _, ok := r.GetArguments()[p]; !ok {
110+
return nil, fmt.Errorf("missing required parameter: %s", p)
111+
}
112+
113+
switch v := r.GetArguments()[p].(type) {
114+
case nil:
115+
return nil, fmt.Errorf("missing required parameter: %s", p)
116+
case []int:
117+
if len(v) == 0 {
118+
return nil, fmt.Errorf("parameter %s cannot be empty", p)
119+
}
120+
return v, nil
121+
case []any:
122+
if len(v) == 0 {
123+
return nil, fmt.Errorf("parameter %s cannot be empty", p)
124+
}
125+
intSlice := make([]int, len(v))
126+
for i, elem := range v {
127+
switch num := elem.(type) {
128+
case float64:
129+
intSlice[i] = int(num)
130+
case int:
131+
intSlice[i] = num
132+
case int32:
133+
intSlice[i] = int(num)
134+
case int64:
135+
intSlice[i] = int(num)
136+
default:
137+
return nil, fmt.Errorf("parameter %s contains non-numeric value, element %d is %T", p, i, elem)
138+
}
139+
}
140+
return intSlice, nil
141+
default:
142+
return nil, fmt.Errorf("parameter %s is not an array, is %T", p, r.GetArguments()[p])
143+
}
144+
}
145+
102146
// OptionalParam is a helper function that can be used to fetch a requested parameter from the request.
103147
// It does the following checks:
104148
// 1. Checks if the parameter is present in the request, if not, it returns its zero-value

pkg/github/server_test.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,110 @@ func TestOptionalIntArrayParam(t *testing.T) {
564564
}
565565
}
566566

567+
func TestRequiredIntArrayParam(t *testing.T) {
568+
tests := []struct {
569+
name string
570+
params map[string]interface{}
571+
paramName string
572+
expected []int
573+
expectError bool
574+
}{
575+
{
576+
name: "parameter not in request",
577+
params: map[string]any{},
578+
paramName: "numbers",
579+
expected: nil,
580+
expectError: true,
581+
},
582+
{
583+
name: "valid any array parameter with float64",
584+
params: map[string]any{
585+
"numbers": []any{float64(1), float64(2), float64(3)},
586+
},
587+
paramName: "numbers",
588+
expected: []int{1, 2, 3},
589+
expectError: false,
590+
},
591+
{
592+
name: "valid int array parameter",
593+
params: map[string]any{
594+
"numbers": []int{1, 2, 3},
595+
},
596+
paramName: "numbers",
597+
expected: []int{1, 2, 3},
598+
expectError: false,
599+
},
600+
{
601+
name: "mixed numeric types",
602+
params: map[string]any{
603+
"numbers": []any{float64(1), int(2), int32(3), int64(4)},
604+
},
605+
paramName: "numbers",
606+
expected: []int{1, 2, 3, 4},
607+
expectError: false,
608+
},
609+
{
610+
name: "invalid type in array",
611+
params: map[string]any{
612+
"numbers": []any{float64(1), "not a number"},
613+
},
614+
paramName: "numbers",
615+
expected: nil,
616+
expectError: true,
617+
},
618+
{
619+
name: "nil value",
620+
params: map[string]any{
621+
"numbers": nil,
622+
},
623+
paramName: "numbers",
624+
expected: nil,
625+
expectError: true,
626+
},
627+
{
628+
name: "empty array",
629+
params: map[string]any{
630+
"numbers": []any{},
631+
},
632+
paramName: "numbers",
633+
expected: nil,
634+
expectError: true,
635+
},
636+
{
637+
name: "empty int array",
638+
params: map[string]any{
639+
"numbers": []int{},
640+
},
641+
paramName: "numbers",
642+
expected: nil,
643+
expectError: true,
644+
},
645+
{
646+
name: "wrong parameter type",
647+
params: map[string]any{
648+
"numbers": "not an array",
649+
},
650+
paramName: "numbers",
651+
expected: nil,
652+
expectError: true,
653+
},
654+
}
655+
656+
for _, tc := range tests {
657+
t.Run(tc.name, func(t *testing.T) {
658+
request := createMCPRequest(tc.params)
659+
result, err := RequiredIntArrayParam(request, tc.paramName)
660+
661+
if tc.expectError {
662+
assert.Error(t, err)
663+
} else {
664+
assert.NoError(t, err)
665+
assert.Equal(t, tc.expected, result)
666+
}
667+
})
668+
}
669+
}
670+
567671
func TestOptionalPaginationParams(t *testing.T) {
568672
tests := []struct {
569673
name string

0 commit comments

Comments
 (0)