@@ -34,6 +34,8 @@ const (
3434 actionsActionTypeGetWorkflowRun
3535 actionsActionTypeGetWorkflowJob
3636 actionsActionTypeGetWorkflowRunUsage
37+ actionsActionTypeGetWorkflowRunLogs
38+ actionsActionTypeGetWorkflowJobLogs
3739 actionsActionTypeDownloadWorkflowArtifact
3840 actionsActionTypeRunWorkflow
3941 actionsActionTypeRerunWorkflowRun
@@ -49,6 +51,8 @@ var actionsResourceTypes = map[actionsActionType]string{
4951 actionsActionTypeGetWorkflow : "get_workflow" ,
5052 actionsActionTypeGetWorkflowRun : "get_workflow_run" ,
5153 actionsActionTypeGetWorkflowJob : "get_workflow_job" ,
54+ actionsActionTypeGetWorkflowRunLogs : "get_workflow_run_logs" ,
55+ actionsActionTypeGetWorkflowJobLogs : "get_job_logs" ,
5256 actionsActionTypeGetWorkflowRunUsage : "get_workflow_run_usage" ,
5357 actionsActionTypeDownloadWorkflowArtifact : "download_workflow_run_artifact" ,
5458 actionsActionTypeRunWorkflow : "run_workflow" ,
@@ -74,7 +78,7 @@ func actionFromString(s string) actionsActionType {
7478 return actionsActionTypeUnknown
7579}
7680
77- func ActionsList (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
81+ func ActionsList (getClient GetClientFn , t translations.TranslationHelperFunc , contentWindowSize int ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
7882 return mcp .NewTool ("actions_list" ,
7983 mcp .WithDescription (t ("TOOL_ACTIONS_LIST_DESCRIPTION" , `Tools for listing GitHub Actions resources.
8084Use this tool to list workflows in a repository, or list workflow runs, jobs, and artifacts for a specific workflow or workflow run.
@@ -247,7 +251,7 @@ Use this tool to list workflows in a repository, or list workflow runs, jobs, an
247251 }
248252}
249253
250- func ActionsGet (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
254+ func ActionsGet (getClient GetClientFn , t translations.TranslationHelperFunc , contentWindowSize int ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
251255 return mcp .NewTool ("actions_get" ,
252256 mcp .WithDescription (t ("TOOL_ACTIONS_READ_DESCRIPTION" , `Tools for reading specific GitHub Actions resources.
253257Use this tool to get details about individual workflows, workflow runs, jobs, and artifacts, by using their unique IDs.
@@ -265,6 +269,8 @@ Use this tool to get details about individual workflows, workflow runs, jobs, an
265269 actionsActionTypeGetWorkflowJob .String (),
266270 actionsActionTypeDownloadWorkflowArtifact .String (),
267271 actionsActionTypeGetWorkflowRunUsage .String (),
272+ actionsActionTypeGetWorkflowRunLogs .String (),
273+ actionsActionTypeGetWorkflowJobLogs .String (),
268274 ),
269275 ),
270276 mcp .WithString ("owner" ,
@@ -276,13 +282,26 @@ Use this tool to get details about individual workflows, workflow runs, jobs, an
276282 mcp .Description (DescriptionRepositoryName ),
277283 ),
278284 mcp .WithString ("resource_id" ,
279- mcp .Required (),
280285 mcp .Description (`The unique identifier of the resource. This will vary based on the "action" provided, so ensure you provide the correct ID:
281286- Provide a workflow ID or workflow file name (e.g. ci.yaml) for 'get_workflow' action.
282- - Provide a workflow run ID for 'get_workflow_run', 'download_workflow_run_artifact' and 'get_workflow_run_usage ' actions.
287+ - Provide a workflow run ID for 'get_workflow_run', 'download_workflow_run_artifact', 'get_workflow_run_usage', and 'get_workflow_run_logs ' actions.
283288- Provide a job ID for the 'get_workflow_job' action.
289+ - Provide a workflow run ID for 'get_job_logs' action when using failed_only parameter.
284290` ),
285291 ),
292+ mcp .WithBoolean ("job_id" ,
293+ mcp .Description ("The ID of the job to get logs for (only for 'get_job_logs' action)" ),
294+ ),
295+ mcp .WithBoolean ("failed_only" ,
296+ mcp .Description ("When true, gets logs for all failed jobs in the workflow run specified by resource_id (only for 'get_job_logs' action)" ),
297+ ),
298+ mcp .WithBoolean ("return_content" ,
299+ mcp .Description ("Returns actual log content instead of URLs (only for 'get_job_logs' action)" ),
300+ ),
301+ mcp .WithNumber ("tail_lines" ,
302+ mcp .Description ("Number of lines to return from the end of the log (only for 'get_job_logs' action)" ),
303+ mcp .DefaultNumber (500 ),
304+ ),
286305 ),
287306 func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
288307 owner , err := RequiredParam [string ](request , "owner" )
@@ -303,7 +322,36 @@ Use this tool to get details about individual workflows, workflow runs, jobs, an
303322 return mcp .NewToolResultError (fmt .Sprintf ("unknown action: %s" , actionTypeStr )), nil
304323 }
305324
306- resourceID , err := RequiredParam [string ](request , "resource_id" )
325+ var resourceID string
326+ if resourceType == actionsActionTypeGetWorkflowJobLogs {
327+ resourceID , err = OptionalParam [string ](request , "resource_id" )
328+ if err != nil {
329+ return mcp .NewToolResultError (err .Error ()), nil
330+ }
331+ } else {
332+ resourceID , err = RequiredParam [string ](request , "resource_id" )
333+ if err != nil {
334+ return mcp .NewToolResultError (err .Error ()), nil
335+ }
336+ }
337+
338+ // Get optional parameters for get_job_logs
339+ jobID , err := OptionalParam [int64 ](request , "job_id" )
340+ if err != nil {
341+ return mcp .NewToolResultError (err .Error ()), nil
342+ }
343+
344+ failedOnly , err := OptionalParam [bool ](request , "failed_only" )
345+ if err != nil {
346+ return mcp .NewToolResultError (err .Error ()), nil
347+ }
348+
349+ returnContent , err := OptionalParam [bool ](request , "return_content" )
350+ if err != nil {
351+ return mcp .NewToolResultError (err .Error ()), nil
352+ }
353+
354+ tailLines , err := OptionalParam [int ](request , "tail_lines" )
307355 if err != nil {
308356 return mcp .NewToolResultError (err .Error ()), nil
309357 }
@@ -320,13 +368,15 @@ Use this tool to get details about individual workflows, workflow runs, jobs, an
320368 // Do nothing, we accept both a string workflow ID or filename
321369 default :
322370 if resourceID == "" {
323- return mcp .NewToolResultError (fmt .Sprintf ("missing required parameter for action %s: resource_id" , actionTypeStr )), nil
324- }
325-
326- // For other actions, resource ID must be an integer
327- resourceIDInt , parseErr = strconv .ParseInt (resourceID , 10 , 64 )
328- if parseErr != nil {
329- return mcp .NewToolResultError (fmt .Sprintf ("invalid resource_id, must be an integer for action %s: %v" , actionTypeStr , parseErr )), nil
371+ if resourceType != actionsActionTypeGetWorkflowJobLogs {
372+ return mcp .NewToolResultError (fmt .Sprintf ("missing required parameter for action %s: resource_id" , actionTypeStr )), nil
373+ }
374+ } else {
375+ // For other actions, resource ID must be an integer
376+ resourceIDInt , parseErr = strconv .ParseInt (resourceID , 10 , 64 )
377+ if parseErr != nil {
378+ return mcp .NewToolResultError (fmt .Sprintf ("invalid resource_id, must be an integer for action %s: %v" , actionTypeStr , parseErr )), nil
379+ }
330380 }
331381 }
332382
@@ -341,6 +391,10 @@ Use this tool to get details about individual workflows, workflow runs, jobs, an
341391 return downloadWorkflowArtifact (ctx , client , request , owner , repo , resourceIDInt )
342392 case actionsActionTypeGetWorkflowRunUsage :
343393 return getWorkflowRunUsage (ctx , client , request , owner , repo , resourceIDInt )
394+ case actionsActionTypeGetWorkflowRunLogs :
395+ return getWorkflowRunLogs (ctx , client , request , owner , repo , resourceIDInt )
396+ case actionsActionTypeGetWorkflowJobLogs :
397+ return getWorkflowJobLogs (ctx , client , request , owner , repo , resourceIDInt , jobID , returnContent , failedOnly , tailLines , contentWindowSize )
344398 case actionsActionTypeUnknown :
345399 return mcp .NewToolResultError (fmt .Sprintf ("unknown action: %s" , actionTypeStr )), nil
346400 default :
@@ -730,163 +784,55 @@ func RunWorkflow(getClient GetClientFn, t translations.TranslationHelperFunc) (t
730784}
731785
732786// GetWorkflowRunLogs creates a tool to download logs for a specific workflow run
733- func GetWorkflowRunLogs (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
734- return mcp .NewTool ("get_workflow_run_logs" ,
735- mcp .WithDescription (t ("TOOL_GET_WORKFLOW_RUN_LOGS_DESCRIPTION" , "Download logs for a specific workflow run (EXPENSIVE: downloads ALL logs as ZIP. Consider using get_job_logs with failed_only=true for debugging failed jobs)" )),
736- mcp .WithToolAnnotation (mcp.ToolAnnotation {
737- Title : t ("TOOL_GET_WORKFLOW_RUN_LOGS_USER_TITLE" , "Get workflow run logs" ),
738- ReadOnlyHint : ToBoolPtr (true ),
739- }),
740- mcp .WithString ("owner" ,
741- mcp .Required (),
742- mcp .Description (DescriptionRepositoryOwner ),
743- ),
744- mcp .WithString ("repo" ,
745- mcp .Required (),
746- mcp .Description (DescriptionRepositoryName ),
747- ),
748- mcp .WithNumber ("run_id" ,
749- mcp .Required (),
750- mcp .Description ("The unique identifier of the workflow run" ),
751- ),
752- ),
753- func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
754- owner , err := RequiredParam [string ](request , "owner" )
755- if err != nil {
756- return mcp .NewToolResultError (err .Error ()), nil
757- }
758- repo , err := RequiredParam [string ](request , "repo" )
759- if err != nil {
760- return mcp .NewToolResultError (err .Error ()), nil
761- }
762- runIDInt , err := RequiredInt (request , "run_id" )
763- if err != nil {
764- return mcp .NewToolResultError (err .Error ()), nil
765- }
766- runID := int64 (runIDInt )
767-
768- client , err := getClient (ctx )
769- if err != nil {
770- return nil , fmt .Errorf ("failed to get GitHub client: %w" , err )
771- }
772-
773- // Get the download URL for the logs
774- url , resp , err := client .Actions .GetWorkflowRunLogs (ctx , owner , repo , runID , 1 )
775- if err != nil {
776- return nil , fmt .Errorf ("failed to get workflow run logs: %w" , err )
777- }
778- defer func () { _ = resp .Body .Close () }()
787+ func getWorkflowRunLogs (ctx context.Context , client * github.Client , _ mcp.CallToolRequest , owner , repo string , runID int64 ) (* mcp.CallToolResult , error ) {
788+ // Get the download URL for the logs
789+ url , resp , err := client .Actions .GetWorkflowRunLogs (ctx , owner , repo , runID , 1 )
790+ if err != nil {
791+ return nil , fmt .Errorf ("failed to get workflow run logs: %w" , err )
792+ }
793+ defer func () { _ = resp .Body .Close () }()
779794
780- // Create response with the logs URL and information
781- result := map [string ]any {
782- "logs_url" : url .String (),
783- "message" : "Workflow run logs are available for download" ,
784- "note" : "The logs_url provides a download link for the complete workflow run logs as a ZIP archive. You can download this archive to extract and examine individual job logs." ,
785- "warning" : "This downloads ALL logs as a ZIP file which can be large and expensive. For debugging failed jobs, consider using get_job_logs with failed_only=true and run_id instead." ,
786- "optimization_tip" : "Use: get_job_logs with parameters {run_id: " + fmt .Sprintf ("%d" , runID ) + ", failed_only: true} for more efficient failed job debugging" ,
787- }
795+ // Create response with the logs URL and information
796+ result := map [string ]any {
797+ "logs_url" : url .String (),
798+ "message" : "Workflow run logs are available for download" ,
799+ "note" : "The logs_url provides a download link for the complete workflow run logs as a ZIP archive. You can download this archive to extract and examine individual job logs." ,
800+ "warning" : "This downloads ALL logs as a ZIP file which can be large and expensive. For debugging failed jobs, consider using get_job_logs with failed_only=true and run_id instead." ,
801+ "optimization_tip" : "Use: get_job_logs with parameters {run_id: " + fmt .Sprintf ("%d" , runID ) + ", failed_only: true} for more efficient failed job debugging" ,
802+ }
788803
789- r , err := json .Marshal (result )
790- if err != nil {
791- return nil , fmt .Errorf ("failed to marshal response: %w" , err )
792- }
804+ r , err := json .Marshal (result )
805+ if err != nil {
806+ return nil , fmt .Errorf ("failed to marshal response: %w" , err )
807+ }
793808
794- return mcp .NewToolResultText (string (r )), nil
795- }
809+ return mcp .NewToolResultText (string (r )), nil
796810}
797811
798- // GetJobLogs creates a tool to download logs for a specific workflow job or efficiently get all failed job logs for a workflow run
799- func GetJobLogs (getClient GetClientFn , t translations.TranslationHelperFunc , contentWindowSize int ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
800- return mcp .NewTool ("get_job_logs" ,
801- mcp .WithDescription (t ("TOOL_GET_JOB_LOGS_DESCRIPTION" , "Download logs for a specific workflow job or efficiently get all failed job logs for a workflow run" )),
802- mcp .WithToolAnnotation (mcp.ToolAnnotation {
803- Title : t ("TOOL_GET_JOB_LOGS_USER_TITLE" , "Get job logs" ),
804- ReadOnlyHint : ToBoolPtr (true ),
805- }),
806- mcp .WithString ("owner" ,
807- mcp .Required (),
808- mcp .Description (DescriptionRepositoryOwner ),
809- ),
810- mcp .WithString ("repo" ,
811- mcp .Required (),
812- mcp .Description (DescriptionRepositoryName ),
813- ),
814- mcp .WithNumber ("job_id" ,
815- mcp .Description ("The unique identifier of the workflow job (required for single job logs)" ),
816- ),
817- mcp .WithNumber ("run_id" ,
818- mcp .Description ("Workflow run ID (required when using failed_only)" ),
819- ),
820- mcp .WithBoolean ("failed_only" ,
821- mcp .Description ("When true, gets logs for all failed jobs in run_id" ),
822- ),
823- mcp .WithBoolean ("return_content" ,
824- mcp .Description ("Returns actual log content instead of URLs" ),
825- ),
826- mcp .WithNumber ("tail_lines" ,
827- mcp .Description ("Number of lines to return from the end of the log" ),
828- mcp .DefaultNumber (500 ),
829- ),
830- ),
831- func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
832- owner , err := RequiredParam [string ](request , "owner" )
833- if err != nil {
834- return mcp .NewToolResultError (err .Error ()), nil
835- }
836- repo , err := RequiredParam [string ](request , "repo" )
837- if err != nil {
838- return mcp .NewToolResultError (err .Error ()), nil
839- }
840-
841- // Get optional parameters
842- jobID , err := OptionalIntParam (request , "job_id" )
843- if err != nil {
844- return mcp .NewToolResultError (err .Error ()), nil
845- }
846- runID , err := OptionalIntParam (request , "run_id" )
847- if err != nil {
848- return mcp .NewToolResultError (err .Error ()), nil
849- }
850- failedOnly , err := OptionalParam [bool ](request , "failed_only" )
851- if err != nil {
852- return mcp .NewToolResultError (err .Error ()), nil
853- }
854- returnContent , err := OptionalParam [bool ](request , "return_content" )
855- if err != nil {
856- return mcp .NewToolResultError (err .Error ()), nil
857- }
858- tailLines , err := OptionalIntParam (request , "tail_lines" )
859- if err != nil {
860- return mcp .NewToolResultError (err .Error ()), nil
861- }
862- // Default to 500 lines if not specified
863- if tailLines == 0 {
864- tailLines = 500
865- }
866-
867- client , err := getClient (ctx )
868- if err != nil {
869- return nil , fmt .Errorf ("failed to get GitHub client: %w" , err )
870- }
812+ // getWorkflowJobLogs downloads logs for a specific workflow job or efficiently gets all failed job logs for a workflow run
813+ func getWorkflowJobLogs (ctx context.Context , client * github.Client , request mcp.CallToolRequest , owner , repo string , runID int64 , jobID int64 , returnContent bool , failedOnly bool , tailLines int , contentWindowSize int ) (* mcp.CallToolResult , error ) {
814+ // Default to 500 lines if not specified
815+ if tailLines == 0 {
816+ tailLines = 500
817+ }
871818
872- // Validate parameters
873- if failedOnly && runID == 0 {
874- return mcp .NewToolResultError ("run_id is required when failed_only is true" ), nil
875- }
876- if ! failedOnly && jobID == 0 {
877- return mcp .NewToolResultError ("job_id is required when failed_only is false" ), nil
878- }
819+ // Validate parameters
820+ if failedOnly && runID == 0 {
821+ return mcp .NewToolResultError ("resource_id is required when failed_only is true" ), nil
822+ }
823+ if ! failedOnly && jobID == 0 {
824+ return mcp .NewToolResultError ("job_id is required when failed_only is false" ), nil
825+ }
879826
880- if failedOnly && runID > 0 {
881- // Handle failed-only mode: get logs for all failed jobs in the workflow run
882- return handleFailedJobLogs (ctx , client , owner , repo , int64 ( runID ) , returnContent , tailLines , contentWindowSize )
883- } else if jobID > 0 {
884- // Handle single job mode
885- return handleSingleJobLogs (ctx , client , owner , repo , int64 (jobID ), returnContent , tailLines , contentWindowSize )
886- }
827+ if failedOnly && runID > 0 {
828+ // Handle failed-only mode: get logs for all failed jobs in the workflow run
829+ return handleFailedJobLogs (ctx , client , owner , repo , runID , returnContent , tailLines , contentWindowSize )
830+ } else if jobID > 0 {
831+ // Handle single job mode
832+ return handleSingleJobLogs (ctx , client , owner , repo , int64 (jobID ), returnContent , tailLines , contentWindowSize )
833+ }
887834
888- return mcp .NewToolResultError ("Either job_id must be provided for single job logs, or run_id with failed_only=true for failed job logs" ), nil
889- }
835+ return mcp .NewToolResultError ("Either job_id must be provided for single job logs, or resource_id with failed_only=true for failed job logs" ), nil
890836}
891837
892838// handleFailedJobLogs gets logs for all failed jobs in a workflow run
0 commit comments