1- //go:build ignore
2-
31package github
42
53import (
@@ -15,10 +13,11 @@ import (
1513 "github.com/github/github-mcp-server/pkg/lockdown"
1614 "github.com/github/github-mcp-server/pkg/sanitize"
1715 "github.com/github/github-mcp-server/pkg/translations"
16+ "github.com/github/github-mcp-server/pkg/utils"
1817 "github.com/go-viper/mapstructure/v2"
1918 "github.com/google/go-github/v79/github"
20- "github.com/mark3labs/mcp -go/mcp "
21- "github.com/mark3labs/mcp-go/server "
19+ "github.com/google/jsonschema -go/jsonschema "
20+ "github.com/modelcontextprotocol/go-sdk/mcp "
2221 "github.com/shurcooL/githubv4"
2322)
2423
@@ -229,85 +228,97 @@ func fragmentToIssue(fragment IssueFragment) *github.Issue {
229228 }
230229}
231230
232- // GetIssue creates a tool to get details of a specific issue in a GitHub repository.
233- func IssueRead (getClient GetClientFn , getGQLClient GetGQLClientFn , t translations.TranslationHelperFunc , flags FeatureFlags ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
234- return mcp .NewTool ("issue_read" ,
235- mcp .WithDescription (t ("TOOL_ISSUE_READ_DESCRIPTION" , "Get information about a specific issue in a GitHub repository." )),
236- mcp .WithToolAnnotation (mcp.ToolAnnotation {
237- Title : t ("TOOL_ISSUE_READ_USER_TITLE" , "Get issue details" ),
238- ReadOnlyHint : ToBoolPtr (true ),
239- }),
240- mcp .WithString ("method" ,
241- mcp .Required (),
242- mcp .Description (`The read operation to perform on a single issue.
231+ // IssueRead creates a tool to get details of a specific issue in a GitHub repository.
232+ func IssueRead (getClient GetClientFn , getGQLClient GetGQLClientFn , t translations.TranslationHelperFunc , flags FeatureFlags ) (mcp.Tool , mcp.ToolHandlerFor [map [string ]any , any ]) {
233+ schema := & jsonschema.Schema {
234+ Type : "object" ,
235+ Properties : map [string ]* jsonschema.Schema {
236+ "method" : {
237+ Type : "string" ,
238+ Description : `The read operation to perform on a single issue.
243239Options are:
2442401. get - Get details of a specific issue.
2452412. get_comments - Get issue comments.
2462423. get_sub_issues - Get sub-issues of the issue.
2472434. get_labels - Get labels assigned to the issue.
248- ` ),
244+ ` ,
245+ Enum : []any {"get" , "get_comments" , "get_sub_issues" , "get_labels" },
246+ },
247+ "owner" : {
248+ Type : "string" ,
249+ Description : "The owner of the repository" ,
250+ },
251+ "repo" : {
252+ Type : "string" ,
253+ Description : "The name of the repository" ,
254+ },
255+ "issue_number" : {
256+ Type : "number" ,
257+ Description : "The number of the issue" ,
258+ },
259+ },
260+ Required : []string {"method" , "owner" , "repo" , "issue_number" },
261+ }
262+ WithPagination (schema )
249263
250- mcp .Enum ("get" , "get_comments" , "get_sub_issues" , "get_labels" ),
251- ),
252- mcp .WithString ("owner" ,
253- mcp .Required (),
254- mcp .Description ("The owner of the repository" ),
255- ),
256- mcp .WithString ("repo" ,
257- mcp .Required (),
258- mcp .Description ("The name of the repository" ),
259- ),
260- mcp .WithNumber ("issue_number" ,
261- mcp .Required (),
262- mcp .Description ("The number of the issue" ),
263- ),
264- WithPagination (),
265- ),
266- func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
267- method , err := RequiredParam [string ](request , "method" )
264+ return mcp.Tool {
265+ Name : "issue_read" ,
266+ Description : t ("TOOL_ISSUE_READ_DESCRIPTION" , "Get information about a specific issue in a GitHub repository." ),
267+ Annotations : & mcp.ToolAnnotations {
268+ Title : t ("TOOL_ISSUE_READ_USER_TITLE" , "Get issue details" ),
269+ ReadOnlyHint : true ,
270+ },
271+ InputSchema : schema ,
272+ },
273+ func (ctx context.Context , _ * mcp.CallToolRequest , args map [string ]any ) (* mcp.CallToolResult , any , error ) {
274+ method , err := RequiredParam [string ](args , "method" )
268275 if err != nil {
269- return mcp .NewToolResultError (err .Error ()), nil
276+ return utils .NewToolResultError (err .Error ()), nil , nil
270277 }
271278
272- owner , err := RequiredParam [string ](request , "owner" )
279+ owner , err := RequiredParam [string ](args , "owner" )
273280 if err != nil {
274- return mcp .NewToolResultError (err .Error ()), nil
281+ return utils .NewToolResultError (err .Error ()), nil , nil
275282 }
276- repo , err := RequiredParam [string ](request , "repo" )
283+ repo , err := RequiredParam [string ](args , "repo" )
277284 if err != nil {
278- return mcp .NewToolResultError (err .Error ()), nil
285+ return utils .NewToolResultError (err .Error ()), nil , nil
279286 }
280- issueNumber , err := RequiredInt (request , "issue_number" )
287+ issueNumber , err := RequiredInt (args , "issue_number" )
281288 if err != nil {
282- return mcp .NewToolResultError (err .Error ()), nil
289+ return utils .NewToolResultError (err .Error ()), nil , nil
283290 }
284291
285- pagination , err := OptionalPaginationParams (request )
292+ pagination , err := OptionalPaginationParams (args )
286293 if err != nil {
287- return mcp .NewToolResultError (err .Error ()), nil
294+ return utils .NewToolResultError (err .Error ()), nil , nil
288295 }
289296
290297 client , err := getClient (ctx )
291298 if err != nil {
292- return nil , fmt . Errorf ("failed to get GitHub client: %w " , err )
299+ return utils . NewToolResultErrorFromErr ("failed to get GitHub client" , err ), nil , nil
293300 }
294301
295302 gqlClient , err := getGQLClient (ctx )
296303 if err != nil {
297- return nil , fmt . Errorf ("failed to get GitHub graphql client: %w " , err )
304+ return utils . NewToolResultErrorFromErr ("failed to get GitHub graphql client" , err ), nil , nil
298305 }
299306
300307 switch method {
301308 case "get" :
302- return GetIssue (ctx , client , gqlClient , owner , repo , issueNumber , flags )
309+ result , err := GetIssue (ctx , client , gqlClient , owner , repo , issueNumber , flags )
310+ return result , nil , err
303311 case "get_comments" :
304- return GetIssueComments (ctx , client , owner , repo , issueNumber , pagination , flags )
312+ result , err := GetIssueComments (ctx , client , owner , repo , issueNumber , pagination , flags )
313+ return result , nil , err
305314 case "get_sub_issues" :
306- return GetSubIssues (ctx , client , owner , repo , issueNumber , pagination , flags )
315+ result , err := GetSubIssues (ctx , client , owner , repo , issueNumber , pagination , flags )
316+ return result , nil , err
307317 case "get_labels" :
308- return GetIssueLabels (ctx , gqlClient , owner , repo , issueNumber , flags )
318+ result , err := GetIssueLabels (ctx , gqlClient , owner , repo , issueNumber , flags )
319+ return result , nil , err
309320 default :
310- return mcp .NewToolResultError (fmt .Sprintf ("unknown method: %s" , method )), nil
321+ return utils .NewToolResultError (fmt .Sprintf ("unknown method: %s" , method )), nil , nil
311322 }
312323 }
313324}
@@ -324,17 +335,17 @@ func GetIssue(ctx context.Context, client *github.Client, gqlClient *githubv4.Cl
324335 if err != nil {
325336 return nil , fmt .Errorf ("failed to read response body: %w" , err )
326337 }
327- return mcp .NewToolResultError (fmt .Sprintf ("failed to get issue: %s" , string (body ))), nil
338+ return utils .NewToolResultError (fmt .Sprintf ("failed to get issue: %s" , string (body ))), nil
328339 }
329340
330341 if flags .LockdownMode {
331342 if issue .User != nil {
332343 shouldRemoveContent , err := lockdown .ShouldRemoveContent (ctx , gqlClient , * issue .User .Login , owner , repo )
333344 if err != nil {
334- return mcp .NewToolResultError (fmt .Sprintf ("failed to check lockdown mode: %v" , err )), nil
345+ return utils .NewToolResultError (fmt .Sprintf ("failed to check lockdown mode: %v" , err )), nil
335346 }
336347 if shouldRemoveContent {
337- return mcp .NewToolResultError ("access to issue details is restricted by lockdown mode" ), nil
348+ return utils .NewToolResultError ("access to issue details is restricted by lockdown mode" ), nil
338349 }
339350 }
340351 }
@@ -354,7 +365,7 @@ func GetIssue(ctx context.Context, client *github.Client, gqlClient *githubv4.Cl
354365 return nil , fmt .Errorf ("failed to marshal issue: %w" , err )
355366 }
356367
357- return mcp .NewToolResultText (string (r )), nil
368+ return utils .NewToolResultText (string (r )), nil
358369}
359370
360371func GetIssueComments (ctx context.Context , client * github.Client , owner string , repo string , issueNumber int , pagination PaginationParams , _ FeatureFlags ) (* mcp.CallToolResult , error ) {
@@ -376,15 +387,15 @@ func GetIssueComments(ctx context.Context, client *github.Client, owner string,
376387 if err != nil {
377388 return nil , fmt .Errorf ("failed to read response body: %w" , err )
378389 }
379- return mcp .NewToolResultError (fmt .Sprintf ("failed to get issue comments: %s" , string (body ))), nil
390+ return utils .NewToolResultError (fmt .Sprintf ("failed to get issue comments: %s" , string (body ))), nil
380391 }
381392
382393 r , err := json .Marshal (comments )
383394 if err != nil {
384395 return nil , fmt .Errorf ("failed to marshal response: %w" , err )
385396 }
386397
387- return mcp .NewToolResultText (string (r )), nil
398+ return utils .NewToolResultText (string (r )), nil
388399}
389400
390401func GetSubIssues (ctx context.Context , client * github.Client , owner string , repo string , issueNumber int , pagination PaginationParams , _ FeatureFlags ) (* mcp.CallToolResult , error ) {
@@ -411,15 +422,15 @@ func GetSubIssues(ctx context.Context, client *github.Client, owner string, repo
411422 if err != nil {
412423 return nil , fmt .Errorf ("failed to read response body: %w" , err )
413424 }
414- return mcp .NewToolResultError (fmt .Sprintf ("failed to list sub-issues: %s" , string (body ))), nil
425+ return utils .NewToolResultError (fmt .Sprintf ("failed to list sub-issues: %s" , string (body ))), nil
415426 }
416427
417428 r , err := json .Marshal (subIssues )
418429 if err != nil {
419430 return nil , fmt .Errorf ("failed to marshal response: %w" , err )
420431 }
421432
422- return mcp .NewToolResultText (string (r )), nil
433+ return utils .NewToolResultText (string (r )), nil
423434}
424435
425436func GetIssueLabels (ctx context.Context , client * githubv4.Client , owner string , repo string , issueNumber int , _ FeatureFlags ) (* mcp.CallToolResult , error ) {
@@ -471,54 +482,60 @@ func GetIssueLabels(ctx context.Context, client *githubv4.Client, owner string,
471482 return nil , fmt .Errorf ("failed to marshal response: %w" , err )
472483 }
473484
474- return mcp .NewToolResultText (string (out )), nil
485+ return utils .NewToolResultText (string (out )), nil
475486
476487}
477488
478489// 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.
479- func ListIssueTypes (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server. ToolHandlerFunc ) {
480-
481- return mcp . NewTool ( "list_issue_types" ,
482- mcp . WithDescription ( t ("TOOL_LIST_ISSUE_TYPES_FOR_ORG" , "List supported issue types for repository owner (organization)." ) ),
483- mcp .WithToolAnnotation (mcp. ToolAnnotation {
490+ func ListIssueTypes (getClient GetClientFn , t translations.TranslationHelperFunc ) (mcp.Tool , mcp. ToolHandlerFor [ map [ string ] any , any ] ) {
491+ return mcp. Tool {
492+ Name : "list_issue_types" ,
493+ Description : t ("TOOL_LIST_ISSUE_TYPES_FOR_ORG" , "List supported issue types for repository owner (organization)." ),
494+ Annotations : & mcp.ToolAnnotations {
484495 Title : t ("TOOL_LIST_ISSUE_TYPES_USER_TITLE" , "List available issue types" ),
485- ReadOnlyHint : ToBoolPtr (true ),
486- }),
487- mcp .WithString ("owner" ,
488- mcp .Required (),
489- mcp .Description ("The organization owner of the repository" ),
490- ),
491- ),
492- func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
493- owner , err := RequiredParam [string ](request , "owner" )
496+ ReadOnlyHint : true ,
497+ },
498+ InputSchema : & jsonschema.Schema {
499+ Type : "object" ,
500+ Properties : map [string ]* jsonschema.Schema {
501+ "owner" : {
502+ Type : "string" ,
503+ Description : "The organization owner of the repository" ,
504+ },
505+ },
506+ Required : []string {"owner" },
507+ },
508+ },
509+ func (ctx context.Context , _ * mcp.CallToolRequest , args map [string ]any ) (* mcp.CallToolResult , any , error ) {
510+ owner , err := RequiredParam [string ](args , "owner" )
494511 if err != nil {
495- return mcp .NewToolResultError (err .Error ()), nil
512+ return utils .NewToolResultError (err .Error ()), nil , nil
496513 }
497514
498515 client , err := getClient (ctx )
499516 if err != nil {
500- return nil , fmt . Errorf ("failed to get GitHub client: %w " , err )
517+ return utils . NewToolResultErrorFromErr ("failed to get GitHub client" , err ), nil , nil
501518 }
502519 issueTypes , resp , err := client .Organizations .ListIssueTypes (ctx , owner )
503520 if err != nil {
504- return nil , fmt . Errorf ("failed to list issue types: %w " , err )
521+ return utils . NewToolResultErrorFromErr ("failed to list issue types" , err ), nil , nil
505522 }
506523 defer func () { _ = resp .Body .Close () }()
507524
508525 if resp .StatusCode != http .StatusOK {
509526 body , err := io .ReadAll (resp .Body )
510527 if err != nil {
511- return nil , fmt . Errorf ("failed to read response body: %w " , err )
528+ return utils . NewToolResultErrorFromErr ("failed to read response body" , err ), nil , nil
512529 }
513- return mcp .NewToolResultError (fmt .Sprintf ("failed to list issue types: %s" , string (body ))), nil
530+ return utils .NewToolResultError (fmt .Sprintf ("failed to list issue types: %s" , string (body ))), nil , nil
514531 }
515532
516533 r , err := json .Marshal (issueTypes )
517534 if err != nil {
518- return nil , fmt . Errorf ("failed to marshal issue types: %w " , err )
535+ return utils . NewToolResultErrorFromErr ("failed to marshal issue types" , err ), nil , nil
519536 }
520537
521- return mcp .NewToolResultText (string (r )), nil
538+ return utils .NewToolResultText (string (r )), nil , nil
522539 }
523540}
524541
0 commit comments