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