@@ -467,6 +467,139 @@ func RemoveSubIssue(getClient GetClientFn, t translations.TranslationHelperFunc)
467
467
}
468
468
}
469
469
470
+ // ReprioritizeSubIssue creates a tool to reprioritize a sub-issue to a different position in the parent list.
471
+ func ReprioritizeSubIssue (getClient GetClientFn ,t translations.TranslationHelperFunc ) (tool mcp.Tool ,handler server.ToolHandlerFunc ) {
472
+ return mcp .NewTool ("reprioritize_sub_issue" ,
473
+ mcp .WithDescription (t ("TOOL_REPRIORITIZE_SUB_ISSUE_DESCRIPTION" ,"Reprioritize a sub-issue to a different position in the parent issue's sub-issue list." )),
474
+ mcp .WithToolAnnotation (mcp.ToolAnnotation {
475
+ Title :t ("TOOL_REPRIORITIZE_SUB_ISSUE_USER_TITLE" ,"Reprioritize sub-issue" ),
476
+ ReadOnlyHint :toBoolPtr (false ),
477
+ }),
478
+ mcp .WithString ("owner" ,
479
+ mcp .Required (),
480
+ mcp .Description ("Repository owner" ),
481
+ ),
482
+ mcp .WithString ("repo" ,
483
+ mcp .Required (),
484
+ mcp .Description ("Repository name" ),
485
+ ),
486
+ mcp .WithNumber ("issue_number" ,
487
+ mcp .Required (),
488
+ mcp .Description ("The number of the parent issue" ),
489
+ ),
490
+ mcp .WithNumber ("sub_issue_id" ,
491
+ mcp .Required (),
492
+ mcp .Description ("The ID of the sub-issue to reprioritize" ),
493
+ ),
494
+ mcp .WithNumber ("after_id" ,
495
+ mcp .Description ("The ID of the sub-issue to be prioritized after (either after_id OR before_id should be specified)" ),
496
+ ),
497
+ mcp .WithNumber ("before_id" ,
498
+ mcp .Description ("The ID of the sub-issue to be prioritized before (either after_id OR before_id should be specified)" ),
499
+ ),
500
+ ),
501
+ func (ctx context.Context ,request mcp.CallToolRequest ) (* mcp.CallToolResult ,error ) {
502
+ owner ,err := requiredParam [string ](request ,"owner" )
503
+ if err != nil {
504
+ return mcp .NewToolResultError (err .Error ()),nil
505
+ }
506
+ repo ,err := requiredParam [string ](request ,"repo" )
507
+ if err != nil {
508
+ return mcp .NewToolResultError (err .Error ()),nil
509
+ }
510
+ issueNumber ,err := RequiredInt (request ,"issue_number" )
511
+ if err != nil {
512
+ return mcp .NewToolResultError (err .Error ()),nil
513
+ }
514
+ subIssueID ,err := RequiredInt (request ,"sub_issue_id" )
515
+ if err != nil {
516
+ return mcp .NewToolResultError (err .Error ()),nil
517
+ }
518
+
519
+ // Handle optional positioning parameters
520
+ afterID ,err := OptionalIntParam (request ,"after_id" )
521
+ if err != nil {
522
+ return mcp .NewToolResultError (err .Error ()),nil
523
+ }
524
+ beforeID ,err := OptionalIntParam (request ,"before_id" )
525
+ if err != nil {
526
+ return mcp .NewToolResultError (err .Error ()),nil
527
+ }
528
+
529
+ // Validate that either after_id or before_id is specified, but not both
530
+ if afterID == 0 && beforeID == 0 {
531
+ return mcp .NewToolResultError ("either after_id or before_id must be specified" ),nil
532
+ }
533
+ if afterID != 0 && beforeID != 0 {
534
+ return mcp .NewToolResultError ("only one of after_id or before_id should be specified, not both" ),nil
535
+ }
536
+
537
+ client ,err := getClient (ctx )
538
+ if err != nil {
539
+ return nil ,fmt .Errorf ("failed to get GitHub client: %w" ,err )
540
+ }
541
+
542
+ // Create the request body
543
+ requestBody := map [string ]interface {}{
544
+ "sub_issue_id" :subIssueID ,
545
+ }
546
+ if afterID != 0 {
547
+ requestBody ["after_id" ]= afterID
548
+ }
549
+ if beforeID != 0 {
550
+ requestBody ["before_id" ]= beforeID
551
+ }
552
+
553
+ // Since the go-github library might not have sub-issues support yet,
554
+ // we'll make a direct HTTP request using the client's HTTP client
555
+ reqBodyBytes ,err := json .Marshal (requestBody )
556
+ if err != nil {
557
+ return nil ,fmt .Errorf ("failed to marshal request body: %w" ,err )
558
+ }
559
+
560
+ url := fmt .Sprintf ("%srepos/%s/%s/issues/%d/sub_issues/priority" ,
561
+ client .BaseURL .String (),owner ,repo ,issueNumber )
562
+ req ,err := http .NewRequestWithContext (ctx ,"PATCH" ,url ,strings .NewReader (string (reqBodyBytes )))
563
+ if err != nil {
564
+ return nil ,fmt .Errorf ("failed to create request: %w" ,err )
565
+ }
566
+
567
+ req .Header .Set ("Accept" ,"application/vnd.github+json" )
568
+ req .Header .Set ("Content-Type" ,"application/json" )
569
+ req .Header .Set ("X-GitHub-Api-Version" ,"2022-11-28" )
570
+
571
+ // Use the same authentication as the GitHub client
572
+ httpClient := client .Client ()
573
+ resp ,err := httpClient .Do (req )
574
+ if err != nil {
575
+ return nil ,fmt .Errorf ("failed to reprioritize sub-issue: %w" ,err )
576
+ }
577
+ defer func () {_ = resp .Body .Close () }()
578
+
579
+ body ,err := io .ReadAll (resp .Body )
580
+ if err != nil {
581
+ return nil ,fmt .Errorf ("failed to read response body: %w" ,err )
582
+ }
583
+
584
+ if resp .StatusCode != http .StatusOK {
585
+ return mcp .NewToolResultError (fmt .Sprintf ("failed to reprioritize sub-issue: %s" ,string (body ))),nil
586
+ }
587
+
588
+ // Parse and re-marshal to ensure consistent formatting
589
+ var result interface {}
590
+ if err := json .Unmarshal (body ,& result );err != nil {
591
+ return nil ,fmt .Errorf ("failed to unmarshal response: %w" ,err )
592
+ }
593
+
594
+ r ,err := json .Marshal (result )
595
+ if err != nil {
596
+ return nil ,fmt .Errorf ("failed to marshal response: %w" ,err )
597
+ }
598
+
599
+ return mcp .NewToolResultText (string (r )),nil
600
+ }
601
+ }
602
+
470
603
// SearchIssues creates a tool to search for issues and pull requests.
471
604
func SearchIssues (getClient GetClientFn ,t translations.TranslationHelperFunc ) (tool mcp.Tool ,handler server.ToolHandlerFunc ) {
472
605
return mcp .NewTool ("search_issues" ,