@@ -26,36 +26,40 @@ type actionsResource int
2626
2727const (
2828 actionsResourceUnknown actionsResource = iota
29- actionsResourceWorkflow
30- actionsResourceWorkflowRun
31- actionsResourceWorkflowJob
29+ actionsResourceGetWorkflow
30+ actionsResourceGetWorkflowRun
31+ actionsResourceGetWorkflowRuns
32+ actionsResourceGetWorkflowJob
33+ actionsResourceGetWorkflowJobs
34+ actionsResourceDownloadWorkflowArtifact
35+ actionsResourceGetWorkflowArtifacts
3236)
3337
38+ var actionsResourceTypes = map [actionsResource ]string {
39+ actionsResourceGetWorkflow : "workflow" ,
40+ actionsResourceGetWorkflowRun : "workflow_run" ,
41+ actionsResourceGetWorkflowRuns : "workflow_runs" ,
42+ actionsResourceGetWorkflowJob : "workflow_job" ,
43+ actionsResourceGetWorkflowJobs : "workflow_jobs" ,
44+ actionsResourceDownloadWorkflowArtifact : "workflow_artifact" ,
45+ actionsResourceGetWorkflowArtifacts : "workflow_artifacts" ,
46+ }
47+
3448func (r actionsResource ) String () string {
35- switch r {
36- case actionsResourceUnknown :
37- return "unknown"
38- case actionsResourceWorkflow :
39- return "workflow"
40- case actionsResourceWorkflowRun :
41- return "workflow_run"
42- case actionsResourceWorkflowJob :
43- return "workflow_job"
49+ if str , ok := actionsResourceTypes [r ]; ok {
50+ return str
4451 }
52+
4553 return "unknown"
4654}
4755
4856func ActionsResourceFromString (s string ) actionsResource {
49- switch strings .ToLower (s ) {
50- case "workflow" :
51- return actionsResourceWorkflow
52- case "workflow_run" :
53- return actionsResourceWorkflowRun
54- case "workflow_job" :
55- return actionsResourceWorkflowJob
56- default :
57- return actionsResourceUnknown
57+ for r , str := range actionsResourceTypes {
58+ if str == strings .ToLower (s ) {
59+ return r
60+ }
5861 }
62+ return actionsResourceUnknown
5963}
6064
6165func ActionsRead (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
@@ -69,9 +73,13 @@ func ActionsRead(getClient GetClientFn, t translations.TranslationHelperFunc) (t
6973 mcp .Required (),
7074 mcp .Description ("The type of Actions resource to read" ),
7175 mcp .Enum (
72- actionsResourceWorkflow .String (),
73- actionsResourceWorkflowRun .String (),
74- actionsResourceWorkflowJob .String (),
76+ actionsResourceGetWorkflow .String (),
77+ actionsResourceGetWorkflowRun .String (),
78+ actionsResourceGetWorkflowRuns .String (),
79+ actionsResourceGetWorkflowJob .String (),
80+ actionsResourceGetWorkflowJobs .String (),
81+ actionsResourceDownloadWorkflowArtifact .String (),
82+ actionsResourceGetWorkflowArtifacts .String (),
7583 ),
7684 ),
7785 mcp .WithString ("owner" ,
@@ -84,8 +92,79 @@ func ActionsRead(getClient GetClientFn, t translations.TranslationHelperFunc) (t
8492 ),
8593 mcp .WithNumber ("resource_id" ,
8694 mcp .Required (),
87- mcp .Description ("The unique identifier of the resource" ),
95+ mcp .Description (`The unique identifier of the resource. This will vary based on the "resource" provided, so ensure you provide the correct ID:
96+ - Provide a workflow ID for 'workflow' and 'workflow_runs' resources.
97+ - Provide a workflow run ID for 'workflow_run', 'workflow_jobs', 'workflow_artifact' and 'workflow_artifacts'.
98+ - Provide a job ID for 'workflow_job' resource.
99+ ` ),
100+ ),
101+ mcp .WithObject ("workflow_runs_filter" ,
102+ mcp .Description ("Filters for workflow runs. **ONLY** used when resource is 'workflow_runs'" ),
103+ mcp .Properties (map [string ]any {
104+ "actor" : map [string ]any {
105+ "type" : "string" ,
106+ "description" : "Returns someone's workflow runs. Use the login for the user who created the workflow run." ,
107+ },
108+ "branch" : map [string ]any {
109+ "type" : "string" ,
110+ "description" : "Returns workflow runs associated with a branch. Use the name of the branch." ,
111+ },
112+ "event" : map [string ]any {
113+ "type" : "string" ,
114+ "description" : "Returns workflow runs for a specific event type" ,
115+ "enum" : []string {
116+ "branch_protection_rule" ,
117+ "check_run" ,
118+ "check_suite" ,
119+ "create" ,
120+ "delete" ,
121+ "deployment" ,
122+ "deployment_status" ,
123+ "discussion" ,
124+ "discussion_comment" ,
125+ "fork" ,
126+ "gollum" ,
127+ "issue_comment" ,
128+ "issues" ,
129+ "label" ,
130+ "merge_group" ,
131+ "milestone" ,
132+ "page_build" ,
133+ "public" ,
134+ "pull_request" ,
135+ "pull_request_review" ,
136+ "pull_request_review_comment" ,
137+ "pull_request_target" ,
138+ "push" ,
139+ "registry_package" ,
140+ "release" ,
141+ "repository_dispatch" ,
142+ "schedule" ,
143+ "status" ,
144+ "watch" ,
145+ "workflow_call" ,
146+ "workflow_dispatch" ,
147+ "workflow_run" ,
148+ },
149+ },
150+ "status" : map [string ]any {
151+ "type" : "string" ,
152+ "description" : "Returns workflow runs with the check run status" ,
153+ "enum" : []string {"queued" , "in_progress" , "completed" , "requested" , "waiting" },
154+ },
155+ }),
156+ ),
157+ mcp .WithObject ("workflow_jobs_filter" ,
158+ mcp .Description ("Filters for workflow jobs. **ONLY** used when resource is 'workflow_jobs'" ),
159+ mcp .Properties (map [string ]any {
160+ "filter" : map [string ]any {
161+ "type" : "string" ,
162+ "description" : "Filters jobs by their completed_at timestamp" ,
163+ "enum" : []string {"latest" , "all" },
164+ },
165+ }),
88166 ),
167+ WithPagination (),
89168 ),
90169 func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
91170 owner , err := RequiredParam [string ](request , "owner" )
@@ -100,25 +179,42 @@ func ActionsRead(getClient GetClientFn, t translations.TranslationHelperFunc) (t
100179 if err != nil {
101180 return mcp .NewToolResultError (err .Error ()), nil
102181 }
182+
103183 resourceType := ActionsResourceFromString (resourceTypeStr )
184+ if resourceType == actionsResourceUnknown {
185+ return mcp .NewToolResultError (fmt .Sprintf ("unknown resource type: %s" , resourceTypeStr )), nil
186+ }
104187
105188 resourceIDInt , err := RequiredInt (request , "resource_id" )
106189 if err != nil {
107190 return mcp .NewToolResultError (err .Error ()), nil
108191 }
109192
193+ pagination , err := OptionalPaginationParams (request )
194+ if err != nil {
195+ return mcp .NewToolResultError (err .Error ()), nil
196+ }
197+
110198 client , err := getClient (ctx )
111199 if err != nil {
112200 return nil , fmt .Errorf ("failed to get GitHub client: %w" , err )
113201 }
114202
115203 switch resourceType {
116- case actionsResourceWorkflow :
117- return getActionsResourceWorkflow (ctx , client , owner , repo , int64 (resourceIDInt ))
118- case actionsResourceWorkflowRun :
119- return nil , fmt .Errorf ("get workflow run by ID not implemented yet" )
120- case actionsResourceWorkflowJob :
121- return nil , fmt .Errorf ("get workflow job by ID not implemented yet" )
204+ case actionsResourceGetWorkflow :
205+ return getActionsResourceWorkflow (ctx , client , request , owner , repo , int64 (resourceIDInt ))
206+ case actionsResourceGetWorkflowRun :
207+ return getActionsResourceWorkflowRun (ctx , client , request , owner , repo , int64 (resourceIDInt ))
208+ case actionsResourceGetWorkflowRuns :
209+ return getActionsResourceWorkflowRuns (ctx , client , request , owner , repo , int64 (resourceIDInt ), pagination )
210+ case actionsResourceGetWorkflowJob :
211+ return getActionsResourceWorkflowJob (ctx , client , request , owner , repo , int64 (resourceIDInt ))
212+ case actionsResourceGetWorkflowJobs :
213+ return getActionsResourceWorkflowJobs (ctx , client , request , owner , repo , int64 (resourceIDInt ), pagination )
214+ case actionsResourceDownloadWorkflowArtifact :
215+ return getActionsResourceDownloadWorkflowArtifact (ctx , client , request , owner , repo , int64 (resourceIDInt ))
216+ case actionsResourceGetWorkflowArtifacts :
217+ return getActionsResourceWorkflowArtifacts (ctx , client , request , owner , repo , int64 (resourceIDInt ), pagination )
122218 case actionsResourceUnknown :
123219 return mcp .NewToolResultError (fmt .Sprintf ("unknown resource type: %s" , resourceTypeStr )), nil
124220 default :
@@ -128,7 +224,7 @@ func ActionsRead(getClient GetClientFn, t translations.TranslationHelperFunc) (t
128224 }
129225}
130226
131- func getActionsResourceWorkflow (ctx context.Context , client * github.Client , owner , repo string , resourceID int64 ) (* mcp.CallToolResult , error ) {
227+ func getActionsResourceWorkflow (ctx context.Context , client * github.Client , _ mcp. CallToolRequest , owner , repo string , resourceID int64 ) (* mcp.CallToolResult , error ) {
132228 workflow , resp , err := client .Actions .GetWorkflowByID (ctx , owner , repo , resourceID )
133229 if err != nil {
134230 return ghErrors .NewGitHubAPIErrorResponse (ctx , "failed to get workflow" , resp , err ), nil
@@ -143,6 +239,150 @@ func getActionsResourceWorkflow(ctx context.Context, client *github.Client, owne
143239 return mcp .NewToolResultText (string (r )), nil
144240}
145241
242+ func getActionsResourceWorkflowRun (ctx context.Context , client * github.Client , _ mcp.CallToolRequest , owner , repo string , resourceID int64 ) (* mcp.CallToolResult , error ) {
243+ workflowRun , resp , err := client .Actions .GetWorkflowRunByID (ctx , owner , repo , resourceID )
244+ if err != nil {
245+ return ghErrors .NewGitHubAPIErrorResponse (ctx , "failed to get workflow run" , resp , err ), nil
246+ }
247+ defer func () { _ = resp .Body .Close () }()
248+ r , err := json .Marshal (workflowRun )
249+ if err != nil {
250+ return nil , fmt .Errorf ("failed to marshal workflow run: %w" , err )
251+ }
252+ return mcp .NewToolResultText (string (r )), nil
253+ }
254+
255+ func getActionsResourceWorkflowRuns (ctx context.Context , client * github.Client , request mcp.CallToolRequest , owner , repo string , resourceID int64 , pagination PaginationParams ) (* mcp.CallToolResult , error ) {
256+ filterArgs , err := OptionalParam [map [string ]any ](request , "workflow_runs_filter" )
257+ if err != nil {
258+ return mcp .NewToolResultError (err .Error ()), nil
259+ }
260+
261+ filterArgsTyped := make (map [string ]string )
262+ for k , v := range filterArgs {
263+ if strVal , ok := v .(string ); ok {
264+ filterArgsTyped [k ] = strVal
265+ } else {
266+ filterArgsTyped [k ] = ""
267+ }
268+ }
269+
270+ workflowRuns , resp , err := client .Actions .ListWorkflowRunsByID (ctx , owner , repo , resourceID , & github.ListWorkflowRunsOptions {
271+ Actor : filterArgsTyped ["actor" ],
272+ Branch : filterArgsTyped ["branch" ],
273+ Event : filterArgsTyped ["event" ],
274+ Status : filterArgsTyped ["status" ],
275+ ListOptions : github.ListOptions {
276+ Page : pagination .Page ,
277+ PerPage : pagination .PerPage ,
278+ },
279+ })
280+ if err != nil {
281+ return ghErrors .NewGitHubAPIErrorResponse (ctx , "failed to list workflow runs" , resp , err ), nil
282+ }
283+
284+ defer func () { _ = resp .Body .Close () }()
285+ r , err := json .Marshal (workflowRuns )
286+ if err != nil {
287+ return nil , fmt .Errorf ("failed to marshal workflow runs: %w" , err )
288+ }
289+
290+ return mcp .NewToolResultText (string (r )), nil
291+ }
292+
293+ func getActionsResourceWorkflowJob (ctx context.Context , client * github.Client , _ mcp.CallToolRequest , owner , repo string , resourceID int64 ) (* mcp.CallToolResult , error ) {
294+ workflowJob , resp , err := client .Actions .GetWorkflowJobByID (ctx , owner , repo , resourceID )
295+ if err != nil {
296+ return ghErrors .NewGitHubAPIErrorResponse (ctx , "failed to get workflow job" , resp , err ), nil
297+ }
298+ defer func () { _ = resp .Body .Close () }()
299+ r , err := json .Marshal (workflowJob )
300+ if err != nil {
301+ return nil , fmt .Errorf ("failed to marshal workflow job: %w" , err )
302+ }
303+ return mcp .NewToolResultText (string (r )), nil
304+ }
305+
306+ func getActionsResourceWorkflowJobs (ctx context.Context , client * github.Client , request mcp.CallToolRequest , owner , repo string , resourceID int64 , pagination PaginationParams ) (* mcp.CallToolResult , error ) {
307+ filterArgs , err := OptionalParam [map [string ]any ](request , "workflow_jobs_filter" )
308+ if err != nil {
309+ return mcp .NewToolResultError (err .Error ()), nil
310+ }
311+
312+ filterArgsTyped := make (map [string ]string )
313+ for k , v := range filterArgs {
314+ if strVal , ok := v .(string ); ok {
315+ filterArgsTyped [k ] = strVal
316+ } else {
317+ filterArgsTyped [k ] = ""
318+ }
319+ }
320+
321+ workflowJobs , resp , err := client .Actions .ListWorkflowJobs (ctx , owner , repo , resourceID , & github.ListWorkflowJobsOptions {
322+ Filter : filterArgsTyped ["filter" ],
323+ ListOptions : github.ListOptions {
324+ Page : pagination .Page ,
325+ PerPage : pagination .PerPage ,
326+ },
327+ })
328+ if err != nil {
329+ return ghErrors .NewGitHubAPIErrorResponse (ctx , "failed to list workflow jobs" , resp , err ), nil
330+ }
331+
332+ defer func () { _ = resp .Body .Close () }()
333+ r , err := json .Marshal (workflowJobs )
334+ if err != nil {
335+ return nil , fmt .Errorf ("failed to marshal workflow jobs: %w" , err )
336+ }
337+
338+ return mcp .NewToolResultText (string (r )), nil
339+ }
340+
341+ func getActionsResourceDownloadWorkflowArtifact (ctx context.Context , client * github.Client , _ mcp.CallToolRequest , owner , repo string , resourceID int64 ) (* mcp.CallToolResult , error ) {
342+ // Get the download URL for the artifact
343+ url , resp , err := client .Actions .DownloadArtifact (ctx , owner , repo , resourceID , 1 )
344+ if err != nil {
345+ return ghErrors .NewGitHubAPIErrorResponse (ctx , "failed to get artifact download URL" , resp , err ), nil
346+ }
347+ defer func () { _ = resp .Body .Close () }()
348+
349+ // Create response with the download URL and information
350+ result := map [string ]any {
351+ "download_url" : url .String (),
352+ "message" : "Artifact is available for download" ,
353+ "note" : "The download_url provides a download link for the artifact as a ZIP archive. The link is temporary and expires after a short time." ,
354+ "artifact_id" : resourceID ,
355+ }
356+
357+ r , err := json .Marshal (result )
358+ if err != nil {
359+ return nil , fmt .Errorf ("failed to marshal response: %w" , err )
360+ }
361+
362+ return mcp .NewToolResultText (string (r )), nil
363+ }
364+
365+ func getActionsResourceWorkflowArtifacts (ctx context.Context , client * github.Client , _ mcp.CallToolRequest , owner , repo string , resourceID int64 , pagination PaginationParams ) (* mcp.CallToolResult , error ) {
366+ // Set up list options
367+ opts := & github.ListOptions {
368+ PerPage : pagination .PerPage ,
369+ Page : pagination .Page ,
370+ }
371+
372+ artifacts , resp , err := client .Actions .ListWorkflowRunArtifacts (ctx , owner , repo , resourceID , opts )
373+ if err != nil {
374+ return ghErrors .NewGitHubAPIErrorResponse (ctx , "failed to list workflow run artifacts" , resp , err ), nil
375+ }
376+ defer func () { _ = resp .Body .Close () }()
377+
378+ r , err := json .Marshal (artifacts )
379+ if err != nil {
380+ return nil , fmt .Errorf ("failed to marshal response: %w" , err )
381+ }
382+
383+ return mcp .NewToolResultText (string (r )), nil
384+ }
385+
146386// ListWorkflows creates a tool to list workflows in a repository
147387func ListWorkflows (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
148388 return mcp .NewTool ("list_workflows" ,
0 commit comments