@@ -683,6 +683,218 @@ func GetIssueComments(getClient GetClientFn, t translations.TranslationHelperFun
683
683
}
684
684
}
685
685
686
+ // GetIssueTimeline creates a tool to get timeline for a GitHub issue.
687
+ func GetIssueTimeline (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
688
+ return mcp .NewTool ("get_issue_timeline" ,
689
+ mcp .WithDescription (t ("TOOL_GET_ISSUE_TIMELINE_DESCRIPTION" , "Get timeline for a GitHub issue" )),
690
+ mcp .WithString ("owner" ,
691
+ mcp .Required (),
692
+ mcp .Description ("Repository owner" ),
693
+ ),
694
+ mcp .WithString ("repo" ,
695
+ mcp .Required (),
696
+ mcp .Description ("Repository name" ),
697
+ ),
698
+ mcp .WithNumber ("issue_number" ,
699
+ mcp .Required (),
700
+ mcp .Description ("Issue number" ),
701
+ ),
702
+ mcp .WithNumber ("page" ,
703
+ mcp .Description ("Page number" ),
704
+ ),
705
+ mcp .WithNumber ("per_page" ,
706
+ mcp .Description ("Number of records per page" ),
707
+ ),
708
+ ),
709
+ func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
710
+ owner , err := requiredParam [string ](request , "owner" )
711
+ if err != nil {
712
+ return mcp .NewToolResultError (err .Error ()), nil
713
+ }
714
+ repo , err := requiredParam [string ](request , "repo" )
715
+ if err != nil {
716
+ return mcp .NewToolResultError (err .Error ()), nil
717
+ }
718
+ issueNumber , err := RequiredInt (request , "issue_number" )
719
+ if err != nil {
720
+ return mcp .NewToolResultError (err .Error ()), nil
721
+ }
722
+ page , err := OptionalIntParamWithDefault (request , "page" , 1 )
723
+ if err != nil {
724
+ return mcp .NewToolResultError (err .Error ()), nil
725
+ }
726
+ perPage , err := OptionalIntParamWithDefault (request , "per_page" , 30 )
727
+ if err != nil {
728
+ return mcp .NewToolResultError (err .Error ()), nil
729
+ }
730
+
731
+ opts := & github.ListOptions {
732
+ Page : page ,
733
+ PerPage : perPage ,
734
+ }
735
+
736
+ client , err := getClient (ctx )
737
+ if err != nil {
738
+ return nil , fmt .Errorf ("failed to get GitHub client: %w" , err )
739
+ }
740
+ events , resp , err := client .Issues .ListIssueTimeline (ctx , owner , repo , issueNumber , opts )
741
+ if err != nil {
742
+ return nil , fmt .Errorf ("failed to get issue timeline: %w" , err )
743
+ }
744
+ defer func () { _ = resp .Body .Close () }()
745
+
746
+ if resp .StatusCode != http .StatusOK {
747
+ body , err := io .ReadAll (resp .Body )
748
+ if err != nil {
749
+ return nil , fmt .Errorf ("failed to read response body: %w" , err )
750
+ }
751
+ return mcp .NewToolResultError (fmt .Sprintf ("failed to get issue timeline: %s" , string (body ))), nil
752
+ }
753
+
754
+ r , err := json .Marshal (events )
755
+ if err != nil {
756
+ return nil , fmt .Errorf ("failed to marshal response: %w" , err )
757
+ }
758
+
759
+ return mcp .NewToolResultText (string (r )), nil
760
+ }
761
+ }
762
+
763
+ // GetIssueEvents creates a tool to get events for a GitHub issue.
764
+ func GetIssueEvents (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
765
+ return mcp .NewTool ("get_issue_events" ,
766
+ mcp .WithDescription (t ("TOOL_GET_ISSUE_EVENTS_DESCRIPTION" , "Get list of events for a GitHub issue" )),
767
+ mcp .WithString ("owner" ,
768
+ mcp .Required (),
769
+ mcp .Description ("Repository owner" ),
770
+ ),
771
+ mcp .WithString ("repo" ,
772
+ mcp .Required (),
773
+ mcp .Description ("Repository name" ),
774
+ ),
775
+ mcp .WithNumber ("issue_number" ,
776
+ mcp .Required (),
777
+ mcp .Description ("Issue number" ),
778
+ ),
779
+ mcp .WithNumber ("page" ,
780
+ mcp .Description ("Page number" ),
781
+ ),
782
+ mcp .WithNumber ("per_page" ,
783
+ mcp .Description ("Number of records per page" ),
784
+ ),
785
+ ),
786
+ func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
787
+ owner , err := requiredParam [string ](request , "owner" )
788
+ if err != nil {
789
+ return mcp .NewToolResultError (err .Error ()), nil
790
+ }
791
+ repo , err := requiredParam [string ](request , "repo" )
792
+ if err != nil {
793
+ return mcp .NewToolResultError (err .Error ()), nil
794
+ }
795
+ issueNumber , err := RequiredInt (request , "issue_number" )
796
+ if err != nil {
797
+ return mcp .NewToolResultError (err .Error ()), nil
798
+ }
799
+ page , err := OptionalIntParamWithDefault (request , "page" , 1 )
800
+ if err != nil {
801
+ return mcp .NewToolResultError (err .Error ()), nil
802
+ }
803
+ perPage , err := OptionalIntParamWithDefault (request , "per_page" , 30 )
804
+ if err != nil {
805
+ return mcp .NewToolResultError (err .Error ()), nil
806
+ }
807
+
808
+ opts := & github.ListOptions {
809
+ Page : page ,
810
+ PerPage : perPage ,
811
+ }
812
+
813
+ client , err := getClient (ctx )
814
+ if err != nil {
815
+ return nil , fmt .Errorf ("failed to get GitHub client: %w" , err )
816
+ }
817
+ events , resp , err := client .Issues .ListIssueEvents (ctx , owner , repo , issueNumber , opts )
818
+ if err != nil {
819
+ return nil , fmt .Errorf ("failed to get issue events: %w" , err )
820
+ }
821
+ defer func () { _ = resp .Body .Close () }()
822
+
823
+ if resp .StatusCode != http .StatusOK {
824
+ body , err := io .ReadAll (resp .Body )
825
+ if err != nil {
826
+ return nil , fmt .Errorf ("failed to read response body: %w" , err )
827
+ }
828
+ return mcp .NewToolResultError (fmt .Sprintf ("failed to get issue events: %s" , string (body ))), nil
829
+ }
830
+
831
+ r , err := json .Marshal (events )
832
+ if err != nil {
833
+ return nil , fmt .Errorf ("failed to marshal response: %w" , err )
834
+ }
835
+
836
+ return mcp .NewToolResultText (string (r )), nil
837
+ }
838
+ }
839
+
840
+ // GetIssueEvent creates a tool to get an event for a GitHub issue.
841
+ func GetIssueEvent (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
842
+ return mcp .NewTool ("get_issue_event" ,
843
+ mcp .WithDescription (t ("TOOL_GET_ISSUE_EVENT_DESCRIPTION" , "Get single event for a GitHub issue" )),
844
+ mcp .WithString ("owner" ,
845
+ mcp .Required (),
846
+ mcp .Description ("Repository owner" ),
847
+ ),
848
+ mcp .WithString ("repo" ,
849
+ mcp .Required (),
850
+ mcp .Description ("Repository name" ),
851
+ ),
852
+ mcp .WithNumber ("event_id" ,
853
+ mcp .Required (),
854
+ mcp .Description ("Event ID" ),
855
+ ),
856
+ ),
857
+ func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
858
+ owner , err := requiredParam [string ](request , "owner" )
859
+ if err != nil {
860
+ return mcp .NewToolResultError (err .Error ()), nil
861
+ }
862
+ repo , err := requiredParam [string ](request , "repo" )
863
+ if err != nil {
864
+ return mcp .NewToolResultError (err .Error ()), nil
865
+ }
866
+ eventID , err := RequiredInt64 (request , "event_id" )
867
+ if err != nil {
868
+ return mcp .NewToolResultError (err .Error ()), nil
869
+ }
870
+
871
+ client , err := getClient (ctx )
872
+ if err != nil {
873
+ return nil , fmt .Errorf ("failed to get GitHub client: %w" , err )
874
+ }
875
+ event , resp , err := client .Issues .GetEvent (ctx , owner , repo , eventID )
876
+ if err != nil {
877
+ return nil , fmt .Errorf ("failed to get issue event: %w" , err )
878
+ }
879
+ defer func () { _ = resp .Body .Close () }()
880
+
881
+ if resp .StatusCode != http .StatusOK {
882
+ body , err := io .ReadAll (resp .Body )
883
+ if err != nil {
884
+ return nil , fmt .Errorf ("failed to read response body: %w" , err )
885
+ }
886
+ return mcp .NewToolResultError (fmt .Sprintf ("failed to get issue event: %s" , string (body ))), nil
887
+ }
888
+
889
+ r , err := json .Marshal (event )
890
+ if err != nil {
891
+ return nil , fmt .Errorf ("failed to marshal response: %w" , err )
892
+ }
893
+
894
+ return mcp .NewToolResultText (string (r )), nil
895
+ }
896
+ }
897
+
686
898
// parseISOTimestamp parses an ISO 8601 timestamp string into a time.Time object.
687
899
// Returns the parsed time or an error if parsing fails.
688
900
// Example formats supported: "2023-01-15T14:30:00Z", "2023-01-15"
0 commit comments