@@ -21,6 +21,11 @@ import (
2121 "github.com/shurcooL/githubv4"
2222)
2323
24+ const (
25+ // DefaultGraphQLPageSize is the default page size for GraphQL queries
26+ DefaultGraphQLPageSize = 30
27+ )
28+
2429// CloseIssueInput represents the input for closing an issue via the GraphQL API.
2530// Used to extend the functionality of the githubv4 library to support closing issues as duplicates.
2631type CloseIssueInput struct {
@@ -335,17 +340,17 @@ func GetIssue(ctx context.Context, client *github.Client, gqlClient *githubv4.Cl
335340 if err != nil {
336341 return nil , fmt .Errorf ("failed to read response body: %w" , err )
337342 }
338- return utils .NewToolResultError (fmt .Sprintf ("failed to get issue: %s" , string (body ))), nil , nil
343+ return utils .NewToolResultError (fmt .Sprintf ("failed to get issue: %s" , string (body ))), nil
339344 }
340345
341346 if flags .LockdownMode {
342347 if issue .User != nil {
343348 shouldRemoveContent , err := lockdown .ShouldRemoveContent (ctx , gqlClient , * issue .User .Login , owner , repo )
344349 if err != nil {
345- return utils .NewToolResultError (fmt .Sprintf ("failed to check lockdown mode: %v" , err )), nil , nil
350+ return utils .NewToolResultError (fmt .Sprintf ("failed to check lockdown mode: %v" , err )), nil
346351 }
347352 if shouldRemoveContent {
348- return utils .NewToolResultError ("access to issue details is restricted by lockdown mode" ), nil , nil
353+ return utils .NewToolResultError ("access to issue details is restricted by lockdown mode" ), nil
349354 }
350355 }
351356 }
@@ -365,7 +370,7 @@ func GetIssue(ctx context.Context, client *github.Client, gqlClient *githubv4.Cl
365370 return nil , fmt .Errorf ("failed to marshal issue: %w" , err )
366371 }
367372
368- return utils .NewToolResultText (string (r )), nil , nil
373+ return utils .NewToolResultText (string (r )), nil
369374}
370375
371376func GetIssueComments (ctx context.Context , client * github.Client , owner string , repo string , issueNumber int , pagination PaginationParams , _ FeatureFlags ) (* mcp.CallToolResult , error ) {
@@ -387,15 +392,15 @@ func GetIssueComments(ctx context.Context, client *github.Client, owner string,
387392 if err != nil {
388393 return nil , fmt .Errorf ("failed to read response body: %w" , err )
389394 }
390- return utils .NewToolResultError (fmt .Sprintf ("failed to get issue comments: %s" , string (body ))), nil , nil
395+ return utils .NewToolResultError (fmt .Sprintf ("failed to get issue comments: %s" , string (body ))), nil
391396 }
392397
393398 r , err := json .Marshal (comments )
394399 if err != nil {
395400 return nil , fmt .Errorf ("failed to marshal response: %w" , err )
396401 }
397402
398- return utils .NewToolResultText (string (r )), nil , nil
403+ return utils .NewToolResultText (string (r )), nil
399404}
400405
401406func GetSubIssues (ctx context.Context , client * github.Client , owner string , repo string , issueNumber int , pagination PaginationParams , _ FeatureFlags ) (* mcp.CallToolResult , error ) {
@@ -422,15 +427,15 @@ func GetSubIssues(ctx context.Context, client *github.Client, owner string, repo
422427 if err != nil {
423428 return nil , fmt .Errorf ("failed to read response body: %w" , err )
424429 }
425- return utils .NewToolResultError (fmt .Sprintf ("failed to list sub-issues: %s" , string (body ))), nil , nil
430+ return utils .NewToolResultError (fmt .Sprintf ("failed to list sub-issues: %s" , string (body ))), nil
426431 }
427432
428433 r , err := json .Marshal (subIssues )
429434 if err != nil {
430435 return nil , fmt .Errorf ("failed to marshal response: %w" , err )
431436 }
432437
433- return utils .NewToolResultText (string (r )), nil , nil
438+ return utils .NewToolResultText (string (r )), nil
434439}
435440
436441func GetIssueLabels (ctx context.Context , client * githubv4.Client , owner string , repo string , issueNumber int , _ FeatureFlags ) (* mcp.CallToolResult , error ) {
@@ -458,7 +463,7 @@ func GetIssueLabels(ctx context.Context, client *githubv4.Client, owner string,
458463 }
459464
460465 if err := client .Query (ctx , & query , vars ); err != nil {
461- return ghErrors .NewGitHubGraphQLErrorResponse (ctx , "Failed to get issue labels" , err ), nil , nil
466+ return ghErrors .NewGitHubGraphQLErrorResponse (ctx , "Failed to get issue labels" , err ), nil
462467 }
463468
464469 // Extract label information
@@ -482,7 +487,7 @@ func GetIssueLabels(ctx context.Context, client *githubv4.Client, owner string,
482487 return nil , fmt .Errorf ("failed to marshal response: %w" , err )
483488 }
484489
485- return utils .NewToolResultText (string (out )), nil , nil
490+ return utils .NewToolResultText (string (out )), nil
486491
487492}
488493
@@ -753,15 +758,15 @@ func AddSubIssue(ctx context.Context, client *github.Client, owner string, repo
753758 if err != nil {
754759 return nil , fmt .Errorf ("failed to read response body: %w" , err )
755760 }
756- return utils .NewToolResultError (fmt .Sprintf ("failed to add sub-issue: %s" , string (body ))), nil , nil
761+ return utils .NewToolResultError (fmt .Sprintf ("failed to add sub-issue: %s" , string (body ))), nil
757762 }
758763
759764 r , err := json .Marshal (subIssue )
760765 if err != nil {
761766 return nil , fmt .Errorf ("failed to marshal response: %w" , err )
762767 }
763768
764- return utils .NewToolResultText (string (r )), nil , nil
769+ return utils .NewToolResultText (string (r )), nil
765770
766771}
767772
@@ -785,24 +790,24 @@ func RemoveSubIssue(ctx context.Context, client *github.Client, owner string, re
785790 if err != nil {
786791 return nil , fmt .Errorf ("failed to read response body: %w" , err )
787792 }
788- return utils .NewToolResultError (fmt .Sprintf ("failed to remove sub-issue: %s" , string (body ))), nil , nil
793+ return utils .NewToolResultError (fmt .Sprintf ("failed to remove sub-issue: %s" , string (body ))), nil
789794 }
790795
791796 r , err := json .Marshal (subIssue )
792797 if err != nil {
793798 return nil , fmt .Errorf ("failed to marshal response: %w" , err )
794799 }
795800
796- return utils .NewToolResultText (string (r )), nil , nil
801+ return utils .NewToolResultText (string (r )), nil
797802}
798803
799804func ReprioritizeSubIssue (ctx context.Context , client * github.Client , owner string , repo string , issueNumber int , subIssueID int , afterID int , beforeID int ) (* mcp.CallToolResult , error ) {
800805 // Validate that either after_id or before_id is specified, but not both
801806 if afterID == 0 && beforeID == 0 {
802- return utils .NewToolResultError ("either after_id or before_id must be specified" ), nil , nil
807+ return utils .NewToolResultError ("either after_id or before_id must be specified" ), nil
803808 }
804809 if afterID != 0 && beforeID != 0 {
805- return utils .NewToolResultError ("only one of after_id or before_id should be specified, not both" ), nil , nil
810+ return utils .NewToolResultError ("only one of after_id or before_id should be specified, not both" ), nil
806811 }
807812
808813 subIssueRequest := github.SubIssueRequest {
@@ -834,15 +839,15 @@ func ReprioritizeSubIssue(ctx context.Context, client *github.Client, owner stri
834839 if err != nil {
835840 return nil , fmt .Errorf ("failed to read response body: %w" , err )
836841 }
837- return utils .NewToolResultError (fmt .Sprintf ("failed to reprioritize sub-issue: %s" , string (body ))), nil , nil
842+ return utils .NewToolResultError (fmt .Sprintf ("failed to reprioritize sub-issue: %s" , string (body ))), nil
838843 }
839844
840845 r , err := json .Marshal (subIssue )
841846 if err != nil {
842847 return nil , fmt .Errorf ("failed to marshal response: %w" , err )
843848 }
844849
845- return utils .NewToolResultText (string (r )), nil , nil
850+ return utils .NewToolResultText (string (r )), nil
846851}
847852
848853// SearchIssues creates a tool to search for issues.
@@ -1087,7 +1092,7 @@ Options are:
10871092
10881093func CreateIssue (ctx context.Context , client * github.Client , owner string , repo string , title string , body string , assignees []string , labels []string , milestoneNum int , issueType string ) (* mcp.CallToolResult , error ) {
10891094 if title == "" {
1090- return utils .NewToolResultError ("missing required parameter: title" ), nil , nil
1095+ return utils .NewToolResultError ("missing required parameter: title" ), nil
10911096 }
10921097
10931098 // Create the issue request
@@ -1117,7 +1122,7 @@ func CreateIssue(ctx context.Context, client *github.Client, owner string, repo
11171122 if err != nil {
11181123 return utils .NewToolResultErrorFromErr ("failed to read response body" , err ), nil
11191124 }
1120- return utils .NewToolResultError (fmt .Sprintf ("failed to create issue: %s" , string (body ))), nil , nil
1125+ return utils .NewToolResultError (fmt .Sprintf ("failed to create issue: %s" , string (body ))), nil
11211126 }
11221127
11231128 // Return minimal response with just essential information
@@ -1131,7 +1136,7 @@ func CreateIssue(ctx context.Context, client *github.Client, owner string, repo
11311136 return utils .NewToolResultErrorFromErr ("failed to marshal response" , err ), nil
11321137 }
11331138
1134- return utils .NewToolResultText (string (r )), nil , nil
1139+ return utils .NewToolResultText (string (r )), nil
11351140}
11361141
11371142func UpdateIssue (ctx context.Context , client * github.Client , gqlClient * githubv4.Client , owner string , repo string , issueNumber int , title string , body string , assignees []string , labels []string , milestoneNum int , issueType string , state string , stateReason string , duplicateOf int ) (* mcp.CallToolResult , error ) {
@@ -1178,20 +1183,20 @@ func UpdateIssue(ctx context.Context, client *github.Client, gqlClient *githubv4
11781183 if err != nil {
11791184 return nil , fmt .Errorf ("failed to read response body: %w" , err )
11801185 }
1181- return utils .NewToolResultError (fmt .Sprintf ("failed to update issue: %s" , string (body ))), nil , nil
1186+ return utils .NewToolResultError (fmt .Sprintf ("failed to update issue: %s" , string (body ))), nil
11821187 }
11831188
11841189 // Use GraphQL API for state updates
11851190 if state != "" {
11861191 // Mandate specifying duplicateOf when trying to close as duplicate
11871192 if state == "closed" && stateReason == "duplicate" && duplicateOf == 0 {
1188- return utils .NewToolResultError ("duplicate_of must be provided when state_reason is 'duplicate'" ), nil , nil
1193+ return utils .NewToolResultError ("duplicate_of must be provided when state_reason is 'duplicate'" ), nil
11891194 }
11901195
11911196 // Get target issue ID (and duplicate issue ID if needed)
11921197 issueID , duplicateIssueID , err := fetchIssueIDs (ctx , gqlClient , owner , repo , issueNumber , duplicateOf )
11931198 if err != nil {
1194- return ghErrors .NewGitHubGraphQLErrorResponse (ctx , "Failed to find issues" , err ), nil , nil
1199+ return ghErrors .NewGitHubGraphQLErrorResponse (ctx , "Failed to find issues" , err ), nil
11951200 }
11961201
11971202 switch state {
@@ -1212,7 +1217,7 @@ func UpdateIssue(ctx context.Context, client *github.Client, gqlClient *githubv4
12121217 IssueID : issueID ,
12131218 }, nil )
12141219 if err != nil {
1215- return ghErrors .NewGitHubGraphQLErrorResponse (ctx , "Failed to reopen issue" , err ), nil , nil
1220+ return ghErrors .NewGitHubGraphQLErrorResponse (ctx , "Failed to reopen issue" , err ), nil
12161221 }
12171222 case "closed" :
12181223 // Use CloseIssue mutation for closing
@@ -1240,7 +1245,7 @@ func UpdateIssue(ctx context.Context, client *github.Client, gqlClient *githubv4
12401245
12411246 err = gqlClient .Mutate (ctx , & mutation , closeInput , nil )
12421247 if err != nil {
1243- return ghErrors .NewGitHubGraphQLErrorResponse (ctx , "Failed to close issue" , err ), nil , nil
1248+ return ghErrors .NewGitHubGraphQLErrorResponse (ctx , "Failed to close issue" , err ), nil
12441249 }
12451250 }
12461251 }
@@ -1256,7 +1261,7 @@ func UpdateIssue(ctx context.Context, client *github.Client, gqlClient *githubv4
12561261 return nil , fmt .Errorf ("failed to marshal response: %w" , err )
12571262 }
12581263
1259- return utils .NewToolResultText (string (r )), nil , nil
1264+ return utils .NewToolResultText (string (r )), nil
12601265}
12611266
12621267// ListIssues creates a tool to list and filter repository issues
@@ -1382,21 +1387,21 @@ func ListIssues(getGQLClient GetGQLClientFn, t translations.TranslationHelperFun
13821387 // Get pagination parameters and convert to GraphQL format
13831388 pagination , err := OptionalCursorPaginationParams (args )
13841389 if err != nil {
1385- return nil , err
1390+ return nil , nil , err
13861391 }
13871392
13881393 // Check if someone tried to use page-based pagination instead of cursor-based
1389- if _ , pageProvided := request . GetArguments () ["page" ]; pageProvided {
1394+ if _ , pageProvided := args ["page" ]; pageProvided {
13901395 return utils .NewToolResultError ("This tool uses cursor-based pagination. Use the 'after' parameter with the 'endCursor' value from the previous response instead of 'page'." ), nil , nil
13911396 }
13921397
13931398 // Check if pagination parameters were explicitly provided
1394- _ , perPageProvided := request . GetArguments () ["perPage" ]
1399+ _ , perPageProvided := args ["perPage" ]
13951400 paginationExplicit := perPageProvided
13961401
13971402 paginationParams , err := pagination .ToGraphQLParams ()
13981403 if err != nil {
1399- return nil , err
1404+ return nil , nil , err
14001405 }
14011406
14021407 // Use default of 30 if pagination was not explicitly provided
@@ -1477,7 +1482,7 @@ func ListIssues(getGQLClient GetGQLClientFn, t translations.TranslationHelperFun
14771482 }
14781483 out , err := json .Marshal (response )
14791484 if err != nil {
1480- return nil , fmt .Errorf ("failed to marshal issues: %w" , err )
1485+ return nil , nil , fmt .Errorf ("failed to marshal issues: %w" , err )
14811486 }
14821487 return utils .NewToolResultText (string (out )), nil , nil
14831488 }
@@ -1563,7 +1568,7 @@ func AssignCopilotToIssue(getGQLClient GetGQLClientFn, t translations.Translatio
15631568
15641569 client , err := getGQLClient (ctx )
15651570 if err != nil {
1566- return nil , fmt .Errorf ("failed to get GitHub client: %w" , err )
1571+ return nil , nil , fmt .Errorf ("failed to get GitHub client: %w" , err )
15671572 }
15681573
15691574 // Firstly, we try to find the copilot bot in the suggested actors for the repository.
@@ -1600,7 +1605,7 @@ func AssignCopilotToIssue(getGQLClient GetGQLClientFn, t translations.Translatio
16001605 var query suggestedActorsQuery
16011606 err := client .Query (ctx , & query , variables )
16021607 if err != nil {
1603- return nil , err
1608+ return nil , nil , err
16041609 }
16051610
16061611 // Iterate all the returned nodes looking for the copilot bot, which is supposed to have the
@@ -1672,7 +1677,7 @@ func AssignCopilotToIssue(getGQLClient GetGQLClientFn, t translations.Translatio
16721677 },
16731678 nil ,
16741679 ); err != nil {
1675- return nil , fmt .Errorf ("failed to replace actors for assignable: %w" , err )
1680+ return nil , nil , fmt .Errorf ("failed to replace actors for assignable: %w" , err )
16761681 }
16771682
16781683 return utils .NewToolResultText ("successfully assigned copilot to issue" ), nil , nil
@@ -1708,63 +1713,54 @@ func parseISOTimestamp(timestamp string) (time.Time, error) {
17081713 return time.Time {}, fmt .Errorf ("invalid ISO 8601 timestamp: %s (supported formats: YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DD)" , timestamp )
17091714}
17101715
1711- func AssignCodingAgentPrompt (t translations.TranslationHelperFunc ) (mcp.Prompt , mcp.PromptHandlerFor [ map [ string ] any ] ) {
1716+ func AssignCodingAgentPrompt (t translations.TranslationHelperFunc ) (mcp.Prompt , mcp.PromptHandler ) {
17121717 return mcp.Prompt {
17131718 Name : "AssignCodingAgent" ,
17141719 Description : t ("PROMPT_ASSIGN_CODING_AGENT_DESCRIPTION" , "Assign GitHub Coding Agent to multiple tasks in a GitHub repository." ),
1715- Arguments : []mcp.PromptArgument {
1720+ Arguments : []* mcp.PromptArgument {
17161721 {
17171722 Name : "repo" ,
17181723 Description : "The repository to assign tasks in (owner/repo)." ,
17191724 Required : true ,
17201725 },
17211726 },
1722- }, func (_ context.Context , _ * mcp.GetPromptRequest , args map [string ]any ) (* mcp.GetPromptResult , error ) {
1723- repo , ok := args ["repo" ].(string )
1724- if ! ok {
1725- repo = ""
1726- }
1727+ }, func (_ context.Context , request * mcp.GetPromptRequest ) (* mcp.GetPromptResult , error ) {
1728+ repo := request .Params .Arguments ["repo" ]
17271729
1728- messages := []mcp.PromptMessage {
1730+ messages := []* mcp.PromptMessage {
17291731 {
1730- Role : mcp .RoleUser ,
1731- Content : mcp.TextContent {
1732- Type : "text" ,
1732+ Role : "user" ,
1733+ Content : & mcp.TextContent {
17331734 Text : "You are a personal assistant for GitHub the Copilot GitHub Coding Agent. Your task is to help the user assign tasks to the Coding Agent based on their open GitHub issues. You can use `assign_copilot_to_issue` tool to assign the Coding Agent to issues that are suitable for autonomous work, and `search_issues` tool to find issues that match the user's criteria. You can also use `list_issues` to get a list of issues in the repository." ,
17341735 },
17351736 },
17361737 {
1737- Role : mcp .RoleUser ,
1738- Content : mcp.TextContent {
1739- Type : "text" ,
1738+ Role : "user" ,
1739+ Content : & mcp.TextContent {
17401740 Text : fmt .Sprintf ("Please go and get a list of the most recent 10 issues from the %s GitHub repository" , repo ),
17411741 },
17421742 },
17431743 {
1744- Role : mcp .RoleAssistant ,
1745- Content : mcp.TextContent {
1746- Type : "text" ,
1744+ Role : "assistant" ,
1745+ Content : & mcp.TextContent {
17471746 Text : fmt .Sprintf ("Sure! I will get a list of the 10 most recent issues for the repo %s." , repo ),
17481747 },
17491748 },
17501749 {
1751- Role : mcp .RoleUser ,
1752- Content : mcp.TextContent {
1753- Type : "text" ,
1750+ Role : "user" ,
1751+ Content : & mcp.TextContent {
17541752 Text : "For each issue, please check if it is a clearly defined coding task with acceptance criteria and a low to medium complexity to identify issues that are suitable for an AI Coding Agent to work on. Then assign each of the identified issues to Copilot." ,
17551753 },
17561754 },
17571755 {
1758- Role : mcp .RoleAssistant ,
1759- Content : mcp.TextContent {
1760- Type : "text" ,
1756+ Role : "assistant" ,
1757+ Content : & mcp.TextContent {
17611758 Text : "Certainly! Let me carefully check which ones are clearly scoped issues that are good to assign to the coding agent, and I will summarize and assign them now." ,
17621759 },
17631760 },
17641761 {
1765- Role : mcp .RoleUser ,
1766- Content : mcp.TextContent {
1767- Type : "text" ,
1762+ Role : "user" ,
1763+ Content : & mcp.TextContent {
17681764 Text : "Great, if you are unsure if an issue is good to assign, ask me first, rather than assigning copilot. If you are certain the issue is clear and suitable you can assign it to Copilot without asking." ,
17691765 },
17701766 },
0 commit comments