9
9
"strconv"
10
10
"strings"
11
11
12
+ ghErrors"github.com/github/github-mcp-server/pkg/errors"
12
13
"github.com/github/github-mcp-server/pkg/translations"
13
14
"github.com/google/go-github/v72/github"
14
15
"github.com/mark3labs/mcp-go/mcp"
@@ -644,7 +645,7 @@ func handleFailedJobLogs(ctx context.Context, client *github.Client, owner, repo
644
645
Filter :"latest" ,
645
646
})
646
647
if err != nil {
647
- return mcp . NewToolResultError ( fmt . Sprintf ( "failed to list workflow jobs: %v " ,err ) ),nil
648
+ return ghErrors . NewGitHubAPIErrorResponse ( ctx , "failed to list workflow jobs" ,resp , err ),nil
648
649
}
649
650
defer func () {_ = resp .Body .Close () }()
650
651
@@ -670,15 +671,18 @@ func handleFailedJobLogs(ctx context.Context, client *github.Client, owner, repo
670
671
// Collect logs for all failed jobs
671
672
var logResults []map [string ]any
672
673
for _ ,job := range failedJobs {
673
- jobResult ,err := getJobLogData (ctx ,client ,owner ,repo ,job .GetID (),job .GetName (),returnContent )
674
+ jobResult ,resp , err := getJobLogData (ctx ,client ,owner ,repo ,job .GetID (),job .GetName (),returnContent )
674
675
if err != nil {
675
676
// Continue with other jobs even if one fails
676
677
jobResult = map [string ]any {
677
678
"job_id" :job .GetID (),
678
679
"job_name" :job .GetName (),
679
680
"error" :err .Error (),
680
681
}
682
+ // Enable reporting of status codes and error causes
683
+ ghErrors .NewGitHubAPIErrorToCtx (ctx ,"failed to get job logs" ,resp ,err )
681
684
}
685
+
682
686
logResults = append (logResults ,jobResult )
683
687
}
684
688
@@ -701,9 +705,9 @@ func handleFailedJobLogs(ctx context.Context, client *github.Client, owner, repo
701
705
702
706
// handleSingleJobLogs gets logs for a single job
703
707
func handleSingleJobLogs (ctx context.Context ,client * github.Client ,owner ,repo string ,jobID int64 ,returnContent bool ) (* mcp.CallToolResult ,error ) {
704
- jobResult ,err := getJobLogData (ctx ,client ,owner ,repo ,jobID ,"" ,returnContent )
708
+ jobResult ,resp , err := getJobLogData (ctx ,client ,owner ,repo ,jobID ,"" ,returnContent )
705
709
if err != nil {
706
- return mcp . NewToolResultError ( err . Error () ),nil
710
+ return ghErrors . NewGitHubAPIErrorResponse ( ctx , "failed to get job logs" , resp , err ),nil
707
711
}
708
712
709
713
r ,err := json .Marshal (jobResult )
@@ -715,11 +719,11 @@ func handleSingleJobLogs(ctx context.Context, client *github.Client, owner, repo
715
719
}
716
720
717
721
// getJobLogData retrieves log data for a single job, either as URL or content
718
- func getJobLogData (ctx context.Context ,client * github.Client ,owner ,repo string ,jobID int64 ,jobName string ,returnContent bool ) (map [string ]any ,error ) {
722
+ func getJobLogData (ctx context.Context ,client * github.Client ,owner ,repo string ,jobID int64 ,jobName string ,returnContent bool ) (map [string ]any ,* github. Response , error ) {
719
723
// Get the download URL for the job logs
720
724
url ,resp ,err := client .Actions .GetWorkflowJobLogs (ctx ,owner ,repo ,jobID ,1 )
721
725
if err != nil {
722
- return nil ,fmt .Errorf ("failed to get job logs for job %d: %w" ,jobID ,err )
726
+ return nil ,resp , fmt .Errorf ("failed to get job logs for job %d: %w" ,jobID ,err )
723
727
}
724
728
defer func () {_ = resp .Body .Close () }()
725
729
@@ -732,9 +736,10 @@ func getJobLogData(ctx context.Context, client *github.Client, owner, repo strin
732
736
733
737
if returnContent {
734
738
// Download and return the actual log content
735
- content ,err := downloadLogContent (url .String ())
739
+ // TODO we can use a generic http error or an interface instead of github.Response
740
+ content ,_ ,err := downloadLogContent (url .String ())
736
741
if err != nil {
737
- return nil ,fmt .Errorf ("failed to download log content for job %d: %w" ,jobID ,err )
742
+ return nil ,nil , fmt .Errorf ("failed to download log content for job %d: %w" ,jobID ,err )
738
743
}
739
744
result ["logs_content" ]= content
740
745
result ["message" ]= "Job logs content retrieved successfully"
@@ -745,29 +750,29 @@ func getJobLogData(ctx context.Context, client *github.Client, owner, repo strin
745
750
result ["note" ]= "The logs_url provides a download link for the individual job logs in plain text format. Use return_content=true to get the actual log content."
746
751
}
747
752
748
- return result ,nil
753
+ return result ,resp , nil
749
754
}
750
755
751
756
// downloadLogContent downloads the actual log content from a GitHub logs URL
752
- func downloadLogContent (logURL string ) (string ,error ) {
757
+ func downloadLogContent (logURL string ) (string ,* http. Response , error ) {
753
758
httpResp ,err := http .Get (logURL )//nolint:gosec // URLs are provided by GitHub API and are safe
754
759
if err != nil {
755
- return "" ,fmt .Errorf ("failed to download logs: %w" ,err )
760
+ return "" ,httpResp , fmt .Errorf ("failed to download logs: %w" ,err )
756
761
}
757
762
defer func () {_ = httpResp .Body .Close () }()
758
763
759
764
if httpResp .StatusCode != http .StatusOK {
760
- return "" ,fmt .Errorf ("failed to download logs: HTTP %d" ,httpResp .StatusCode )
765
+ return "" ,httpResp , fmt .Errorf ("failed to download logs: HTTP %d" ,httpResp .StatusCode )
761
766
}
762
767
763
768
content ,err := io .ReadAll (httpResp .Body )
764
769
if err != nil {
765
- return "" ,fmt .Errorf ("failed to read log content: %w" ,err )
770
+ return "" ,httpResp , fmt .Errorf ("failed to read log content: %w" ,err )
766
771
}
767
772
768
773
// Clean up and format the log content for better readability
769
774
logContent := strings .TrimSpace (string (content ))
770
- return logContent ,nil
775
+ return logContent ,httpResp , nil
771
776
}
772
777
773
778
// RerunWorkflowRun creates a tool to re-run an entire workflow run
@@ -813,7 +818,7 @@ func RerunWorkflowRun(getClient GetClientFn, t translations.TranslationHelperFun
813
818
814
819
resp ,err := client .Actions .RerunWorkflowByID (ctx ,owner ,repo ,runID )
815
820
if err != nil {
816
- return nil , fmt . Errorf ( "failed to rerun workflow run: %w " ,err )
821
+ return ghErrors . NewGitHubAPIErrorResponse ( ctx , "failed to rerun workflow run" ,resp , err ), nil
817
822
}
818
823
defer func () {_ = resp .Body .Close () }()
819
824
@@ -876,7 +881,7 @@ func RerunFailedJobs(getClient GetClientFn, t translations.TranslationHelperFunc
876
881
877
882
resp ,err := client .Actions .RerunFailedJobsByID (ctx ,owner ,repo ,runID )
878
883
if err != nil {
879
- return nil , fmt . Errorf ( "failed to rerun failed jobs: %w " ,err )
884
+ return ghErrors . NewGitHubAPIErrorResponse ( ctx , "failed to rerun failed jobs" ,resp , err ), nil
880
885
}
881
886
defer func () {_ = resp .Body .Close () }()
882
887
@@ -939,7 +944,7 @@ func CancelWorkflowRun(getClient GetClientFn, t translations.TranslationHelperFu
939
944
940
945
resp ,err := client .Actions .CancelWorkflowRunByID (ctx ,owner ,repo ,runID )
941
946
if err != nil {
942
- return nil , fmt . Errorf ( "failed to cancel workflow run: %w " ,err )
947
+ return ghErrors . NewGitHubAPIErrorResponse ( ctx , "failed to cancel workflow run" ,resp , err ), nil
943
948
}
944
949
defer func () {_ = resp .Body .Close () }()
945
950
@@ -1024,7 +1029,7 @@ func ListWorkflowRunArtifacts(getClient GetClientFn, t translations.TranslationH
1024
1029
1025
1030
artifacts ,resp ,err := client .Actions .ListWorkflowRunArtifacts (ctx ,owner ,repo ,runID ,opts )
1026
1031
if err != nil {
1027
- return nil , fmt . Errorf ( "failed to list workflow run artifacts: %w " ,err )
1032
+ return ghErrors . NewGitHubAPIErrorResponse ( ctx , "failed to list workflow run artifacts" ,resp , err ), nil
1028
1033
}
1029
1034
defer func () {_ = resp .Body .Close () }()
1030
1035
@@ -1081,7 +1086,7 @@ func DownloadWorkflowRunArtifact(getClient GetClientFn, t translations.Translati
1081
1086
// Get the download URL for the artifact
1082
1087
url ,resp ,err := client .Actions .DownloadArtifact (ctx ,owner ,repo ,artifactID ,1 )
1083
1088
if err != nil {
1084
- return nil , fmt . Errorf ( "failed to get artifact download URL: %w " ,err )
1089
+ return ghErrors . NewGitHubAPIErrorResponse ( ctx , "failed to get artifact download URL" ,resp , err ), nil
1085
1090
}
1086
1091
defer func () {_ = resp .Body .Close () }()
1087
1092
@@ -1146,7 +1151,7 @@ func DeleteWorkflowRunLogs(getClient GetClientFn, t translations.TranslationHelp
1146
1151
1147
1152
resp ,err := client .Actions .DeleteWorkflowRunLogs (ctx ,owner ,repo ,runID )
1148
1153
if err != nil {
1149
- return nil , fmt . Errorf ( "failed to delete workflow run logs: %w " ,err )
1154
+ return ghErrors . NewGitHubAPIErrorResponse ( ctx , "failed to delete workflow run logs" ,resp , err ), nil
1150
1155
}
1151
1156
defer func () {_ = resp .Body .Close () }()
1152
1157
@@ -1209,7 +1214,7 @@ func GetWorkflowRunUsage(getClient GetClientFn, t translations.TranslationHelper
1209
1214
1210
1215
usage ,resp ,err := client .Actions .GetWorkflowRunUsageByID (ctx ,owner ,repo ,runID )
1211
1216
if err != nil {
1212
- return nil , fmt . Errorf ( "failed to get workflow run usage: %w " ,err )
1217
+ return ghErrors . NewGitHubAPIErrorResponse ( ctx , "failed to get workflow run usage" ,resp , err ), nil
1213
1218
}
1214
1219
defer func () {_ = resp .Body .Close () }()
1215
1220