5
5
"encoding/json"
6
6
"fmt"
7
7
"io"
8
- "math"
9
8
"net/http"
10
9
"strings"
11
10
"time"
@@ -1355,6 +1354,11 @@ type ClosingPullRequest struct {
1355
1354
Merged bool `json:"merged"`
1356
1355
}
1357
1356
1357
+ const (
1358
+ // DefaultClosingPRsLimit is the default number of closing PRs to return per issue
1359
+ DefaultClosingPRsLimit = 10
1360
+ )
1361
+
1358
1362
// FindClosingPullRequests creates a tool to find pull requests that closed specific issues
1359
1363
func FindClosingPullRequests (getGQLClient GetGQLClientFn , t translations.TranslationHelperFunc ) (mcp.Tool , server.ToolHandlerFunc ) {
1360
1364
return mcp .NewTool ("find_closing_pull_requests" ,
@@ -1404,15 +1408,17 @@ func FindClosingPullRequests(getGQLClient GetGQLClientFn, t translations.Transla
1404
1408
),
1405
1409
func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
1406
1410
// Parse pagination parameters
1407
- limit := 10 // default
1411
+ limit := DefaultClosingPRsLimit // default
1412
+ limitExplicitlySet := false
1408
1413
if limitParam , exists := request .GetArguments ()["limit" ]; exists {
1414
+ limitExplicitlySet = true
1409
1415
if limitFloat , ok := limitParam .(float64 ); ok {
1410
1416
limit = int (limitFloat )
1411
1417
if limit <= 0 || limit > 100 {
1412
- return mcp .NewToolResultError ("limit must be between 1 and 100" ), nil
1418
+ return mcp .NewToolResultError ("limit must be between 1 and 100 inclusive " ), nil
1413
1419
}
1414
1420
} else {
1415
- return mcp .NewToolResultError ("limit must be a number" ), nil
1421
+ return mcp .NewToolResultError ("limit must be a number between 1 and 100 " ), nil
1416
1422
}
1417
1423
}
1418
1424
@@ -1422,7 +1428,7 @@ func FindClosingPullRequests(getGQLClient GetGQLClientFn, t translations.Transla
1422
1428
return mcp .NewToolResultError (fmt .Sprintf ("last parameter error: %s" , err .Error ())), nil
1423
1429
}
1424
1430
if last != 0 && (last <= 0 || last > 100 ) {
1425
- return mcp .NewToolResultError ("last must be between 1 and 100" ), nil
1431
+ return mcp .NewToolResultError ("last must be between 1 and 100 inclusive for backward pagination " ), nil
1426
1432
}
1427
1433
1428
1434
// Parse cursor parameters
@@ -1436,17 +1442,17 @@ func FindClosingPullRequests(getGQLClient GetGQLClientFn, t translations.Transla
1436
1442
}
1437
1443
1438
1444
// Validate pagination parameter combinations
1439
- if last != 0 && limit != 10 {
1440
- return mcp .NewToolResultError ("cannot use both 'limit' and 'last' parameters together" ), nil
1445
+ if last != 0 && limitExplicitlySet {
1446
+ return mcp .NewToolResultError ("cannot use both 'limit' and 'last' parameters together - use 'limit' for forward pagination or 'last' for backward pagination " ), nil
1441
1447
}
1442
1448
if after != "" && before != "" {
1443
- return mcp .NewToolResultError ("cannot use both 'after' and 'before' cursors together" ), nil
1449
+ return mcp .NewToolResultError ("cannot use both 'after' and 'before' cursors together - use 'after' for forward pagination or 'before' for backward pagination " ), nil
1444
1450
}
1445
1451
if before != "" && last == 0 {
1446
- return mcp .NewToolResultError ("'before' cursor requires 'last' parameter" ), nil
1452
+ return mcp .NewToolResultError ("'before' cursor requires 'last' parameter for backward pagination " ), nil
1447
1453
}
1448
1454
if after != "" && last != 0 {
1449
- return mcp .NewToolResultError ("'after' cursor cannot be used with 'last' parameter" ), nil
1455
+ return mcp .NewToolResultError ("'after' cursor cannot be used with 'last' parameter - use 'after' with 'limit' for forward pagination " ), nil
1450
1456
}
1451
1457
1452
1458
// Parse filtering parameters
@@ -1479,7 +1485,7 @@ func FindClosingPullRequests(getGQLClient GetGQLClientFn, t translations.Transla
1479
1485
1480
1486
// Validate that issue_numbers is not empty
1481
1487
if len (issueNumbers ) == 0 {
1482
- return mcp .NewToolResultError ("issue_numbers parameter is required and cannot be empty" ), nil
1488
+ return mcp .NewToolResultError ("issue_numbers parameter is required and cannot be empty - provide at least one issue number " ), nil
1483
1489
}
1484
1490
1485
1491
// Get GraphQL client
@@ -1559,35 +1565,31 @@ func queryClosingPRsForIssueEnhanced(ctx context.Context, client *githubv4.Clien
1559
1565
} `graphql:"repository(owner: $owner, name: $repo)"`
1560
1566
}
1561
1567
1562
- // Validate issue number
1563
- if issueNumber < 0 || issueNumber > math . MaxInt32 {
1568
+ // Validate issue number (basic bounds check)
1569
+ if issueNumber < 0 {
1564
1570
return nil , fmt .Errorf ("issue number %d is out of valid range" , issueNumber )
1565
1571
}
1566
- issueNumber32 := int32 (issueNumber ) // safe: range-checked above
1567
1572
1568
- // Validate pagination
1569
- if params .Last != 0 && ( params . Last < 0 || params . Last > math . MaxInt32 ) {
1573
+ // Validate pagination parameters (basic bounds check)
1574
+ if params .Last < 0 {
1570
1575
return nil , fmt .Errorf ("last parameter %d is out of valid range" , params .Last )
1571
1576
}
1572
- if params .First < 0 || params . First > math . MaxInt32 {
1577
+ if params .First < 0 {
1573
1578
return nil , fmt .Errorf ("first parameter %d is out of valid range" , params .First )
1574
1579
}
1575
1580
1576
- first32 := int32 (params .First )
1577
- last32 := int32 (params .Last )
1578
-
1579
1581
// Build variables map
1580
1582
variables := map [string ]any {
1581
1583
"owner" : githubv4 .String (owner ),
1582
1584
"repo" : githubv4 .String (repo ),
1583
- "number" : githubv4 .Int (issueNumber32 ),
1585
+ "number" : githubv4 .Int (issueNumber ), // #nosec G115 - issueNumber validated to be positive
1584
1586
}
1585
1587
1586
- if last32 != 0 {
1587
- variables ["last" ] = githubv4 .Int (last32 )
1588
+ if params . Last != 0 {
1589
+ variables ["last" ] = githubv4 .Int (params . Last ) // #nosec G115 - params.Last validated to be positive
1588
1590
variables ["first" ] = (* githubv4 .Int )(nil )
1589
1591
} else {
1590
- variables ["first" ] = githubv4 .Int (first32 )
1592
+ variables ["first" ] = githubv4 .Int (params . First ) // #nosec G115 - params.First validated to be positive
1591
1593
variables ["last" ] = (* githubv4 .Int )(nil )
1592
1594
}
1593
1595
0 commit comments