@@ -206,6 +206,84 @@ func GetIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (tool
206
206
}
207
207
}
208
208
209
+ // SearchMilestones creates a tool to search for milestones in a repository.
210
+ func SearchMilestones (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
211
+ return mcp .NewTool ("search_milestones" ,
212
+ mcp .WithDescription (t ("TOOL_SEARCH_MILESTONES_DESCRIPTION" , "Search for milestones in a repository." )),
213
+ mcp .WithToolAnnotation (mcp.ToolAnnotation {
214
+ Title : t ("TOOL_SEARCH_MILESTONES_USER_TITLE" , "Search milestones" ),
215
+ ReadOnlyHint : ToBoolPtr (true ),
216
+ }),
217
+ mcp .WithString ("owner" ,
218
+ mcp .Required (),
219
+ mcp .Description ("Repository owner" ),
220
+ ),
221
+ mcp .WithString ("repo" ,
222
+ mcp .Required (),
223
+ mcp .Description ("Repository name" ),
224
+ ),
225
+ mcp .WithString ("query" ,
226
+ mcp .Required (),
227
+ mcp .Description ("Search query to filter milestones by title or description" ),
228
+ ),
229
+ mcp .WithString ("state" ,
230
+ mcp .Description ("Filter by state" ),
231
+ mcp .Enum ("open" , "closed" , "all" ),
232
+ ),
233
+ ),
234
+ func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
235
+ owner , err := RequiredParam [string ](request , "owner" )
236
+ if err != nil {
237
+ return mcp .NewToolResultError (err .Error ()), nil
238
+ }
239
+ repo , err := RequiredParam [string ](request , "repo" )
240
+ if err != nil {
241
+ return mcp .NewToolResultError (err .Error ()), nil
242
+ }
243
+ query , err := RequiredParam [string ](request , "query" )
244
+ if err != nil {
245
+ return mcp .NewToolResultError (err .Error ()), nil
246
+ }
247
+ state , err := OptionalParam [string ](request , "state" )
248
+ if err != nil {
249
+ return mcp .NewToolResultError (err .Error ()), nil
250
+ }
251
+
252
+ if state == "" {
253
+ state = "open"
254
+ }
255
+
256
+ opts := & github.MilestoneListOptions {
257
+ State : state ,
258
+ }
259
+
260
+ client , err := getClient (ctx )
261
+ if err != nil {
262
+ return nil , fmt .Errorf ("failed to get GitHub client: %w" , err )
263
+ }
264
+
265
+ allMilestones , _ , err := client .Issues .ListMilestones (ctx , owner , repo , opts )
266
+ if err != nil {
267
+ return nil , fmt .Errorf ("failed to list milestones: %w" , err )
268
+ }
269
+
270
+ var filteredMilestones []* github.Milestone
271
+ for _ , milestone := range allMilestones {
272
+ if (milestone .Title != nil && strings .Contains (strings .ToLower (* milestone .Title ), strings .ToLower (query ))) ||
273
+ (milestone .Description != nil && strings .Contains (strings .ToLower (* milestone .Description ), strings .ToLower (query ))) {
274
+ filteredMilestones = append (filteredMilestones , milestone )
275
+ }
276
+ }
277
+
278
+ r , err := json .Marshal (filteredMilestones )
279
+ if err != nil {
280
+ return nil , fmt .Errorf ("failed to marshal response: %w" , err )
281
+ }
282
+
283
+ return mcp .NewToolResultText (string (r )), nil
284
+ }
285
+ }
286
+
209
287
// EditMilestone creates a tool to edit an existing milestone in a GitHub repository.
210
288
func EditMilestone (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
211
289
return mcp .NewTool ("edit_milestone" ,
@@ -474,6 +552,100 @@ func DeleteMilestone(getClient GetClientFn, t translations.TranslationHelperFunc
474
552
}
475
553
}
476
554
555
+ // ListMilestones creates a tool to list milestones for a repository.
556
+ func ListMilestones (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
557
+ return mcp .NewTool ("list_milestones" ,
558
+ mcp .WithDescription (t ("TOOL_LIST_MILESTONES_DESCRIPTION" , "List milestones for a repository." )),
559
+ mcp .WithToolAnnotation (mcp.ToolAnnotation {
560
+ Title : t ("TOOL_LIST_MILESTONES_USER_TITLE" , "List milestones" ),
561
+ ReadOnlyHint : ToBoolPtr (true ),
562
+ }),
563
+ mcp .WithString ("owner" ,
564
+ mcp .Required (),
565
+ mcp .Description ("Repository owner" ),
566
+ ),
567
+ mcp .WithString ("repo" ,
568
+ mcp .Required (),
569
+ mcp .Description ("Repository name" ),
570
+ ),
571
+ mcp .WithString ("state" ,
572
+ mcp .Description ("Filter by state" ),
573
+ mcp .Enum ("open" , "closed" , "all" ),
574
+ ),
575
+ mcp .WithString ("sort" ,
576
+ mcp .Description ("Sort field" ),
577
+ mcp .Enum ("due_on" , "completeness" ),
578
+ ),
579
+ mcp .WithString ("direction" ,
580
+ mcp .Description ("Sort direction" ),
581
+ mcp .Enum ("asc" , "desc" ),
582
+ ),
583
+ WithPagination (),
584
+ ),
585
+ func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
586
+ owner , err := RequiredParam [string ](request , "owner" )
587
+ if err != nil {
588
+ return mcp .NewToolResultError (err .Error ()), nil
589
+ }
590
+ repo , err := RequiredParam [string ](request , "repo" )
591
+ if err != nil {
592
+ return mcp .NewToolResultError (err .Error ()), nil
593
+ }
594
+ state , err := OptionalParam [string ](request , "state" )
595
+ if err != nil {
596
+ return mcp .NewToolResultError (err .Error ()), nil
597
+ }
598
+ sort , err := OptionalParam [string ](request , "sort" )
599
+ if err != nil {
600
+ return mcp .NewToolResultError (err .Error ()), nil
601
+ }
602
+ direction , err := OptionalParam [string ](request , "direction" )
603
+ if err != nil {
604
+ return mcp .NewToolResultError (err .Error ()), nil
605
+ }
606
+ pagination , err := OptionalPaginationParams (request )
607
+ if err != nil {
608
+ return mcp .NewToolResultError (err .Error ()), nil
609
+ }
610
+
611
+ opts := & github.MilestoneListOptions {
612
+ State : state ,
613
+ Sort : sort ,
614
+ Direction : direction ,
615
+ ListOptions : github.ListOptions {
616
+ Page : pagination .Page ,
617
+ PerPage : pagination .PerPage ,
618
+ },
619
+ }
620
+
621
+ client , err := getClient (ctx )
622
+ if err != nil {
623
+ return nil , fmt .Errorf ("failed to get GitHub client: %w" , err )
624
+ }
625
+
626
+ milestones , resp , err := client .Issues .ListMilestones (ctx , owner , repo , opts )
627
+ if err != nil {
628
+ return nil , fmt .Errorf ("failed to list milestones: %w" , err )
629
+ }
630
+ defer func () { _ = resp .Body .Close () }()
631
+
632
+ if resp .StatusCode != http .StatusOK {
633
+ body , err := io .ReadAll (resp .Body )
634
+ if err != nil {
635
+ return nil , fmt .Errorf ("failed to read response body: %w" , err )
636
+ }
637
+ return mcp .NewToolResultError (fmt .Sprintf ("failed to list milestones: %s" , string (body ))), nil
638
+ }
639
+
640
+ r , err := json .Marshal (milestones )
641
+ if err != nil {
642
+ return nil , fmt .Errorf ("failed to marshal response: %w" , err )
643
+ }
644
+
645
+ return mcp .NewToolResultText (string (r )), nil
646
+ }
647
+ }
648
+
477
649
// ListIssueTypes creates a tool to list defined issue types for an organization. This can be used to understand supported issue type values for creating or updating issues.
478
650
func ListIssueTypes (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
479
651
0 commit comments