diff --git a/github-mcp-server b/github-mcp-server new file mode 100755 index 000000000..864242c24 Binary files /dev/null and b/github-mcp-server differ diff --git a/pkg/github/__toolsnaps__/list_issues.snap b/pkg/github/__toolsnaps__/list_issues.snap index f63da9c85..5475988c2 100644 --- a/pkg/github/__toolsnaps__/list_issues.snap +++ b/pkg/github/__toolsnaps__/list_issues.snap @@ -29,7 +29,8 @@ "description": "Order issues by field. If provided, the 'direction' also needs to be provided.", "enum": [ "CREATED_AT", - "UPDATED_AT" + "UPDATED_AT", + "COMMENTS" ], "type": "string" }, diff --git a/pkg/github/issues.go b/pkg/github/issues.go index ad0a0749b..4965b7d7e 100644 --- a/pkg/github/issues.go +++ b/pkg/github/issues.go @@ -38,6 +38,9 @@ type IssueFragment struct { Description githubv4.String } } `graphql:"labels(first: 100)"` + Comments struct { + TotalCount githubv4.Int + } `graphql:"comments"` } // Common interface for all issue query types @@ -133,10 +136,11 @@ func fragmentToIssue(fragment IssueFragment) *github.Issue { User: &github.User{ Login: github.Ptr(string(fragment.Author.Login)), }, - State: github.Ptr(string(fragment.State)), - ID: github.Ptr(fragment.DatabaseID), - Body: github.Ptr(string(fragment.Body)), - Labels: foundLabels, + State: github.Ptr(string(fragment.State)), + ID: github.Ptr(fragment.DatabaseID), + Body: github.Ptr(string(fragment.Body)), + Labels: foundLabels, + Comments: github.Ptr(int(fragment.Comments.TotalCount)), } } @@ -875,7 +879,7 @@ func ListIssues(getGQLClient GetGQLClientFn, t translations.TranslationHelperFun ), mcp.WithString("orderBy", mcp.Description("Order issues by field. If provided, the 'direction' also needs to be provided."), - mcp.Enum("CREATED_AT", "UPDATED_AT"), + mcp.Enum("CREATED_AT", "UPDATED_AT", "COMMENTS"), ), mcp.WithString("direction", mcp.Description("Order direction. If provided, the 'orderBy' also needs to be provided."), diff --git a/pkg/github/issues_test.go b/pkg/github/issues_test.go index 2a530ef48..149ec1970 100644 --- a/pkg/github/issues_test.go +++ b/pkg/github/issues_test.go @@ -681,6 +681,9 @@ func Test_ListIssues(t *testing.T) { {"name": "bug", "id": "label1", "description": "Bug label"}, }, }, + "comments": map[string]any{ + "totalCount": 5, + }, }, { "number": 456, @@ -696,6 +699,9 @@ func Test_ListIssues(t *testing.T) { {"name": "enhancement", "id": "label2", "description": "Enhancement label"}, }, }, + "comments": map[string]any{ + "totalCount": 3, + }, }, } @@ -713,6 +719,9 @@ func Test_ListIssues(t *testing.T) { "labels": map[string]any{ "nodes": []map[string]any{}, }, + "comments": map[string]any{ + "totalCount": 1, + }, }, } @@ -875,8 +884,8 @@ func Test_ListIssues(t *testing.T) { } // Define the actual query strings that match the implementation - qBasicNoLabels := "query($after:String$direction:OrderDirection!$first:Int!$orderBy:IssueOrderField!$owner:String!$repo:String!$states:[IssueState!]!){repository(owner: $owner, name: $repo){issues(first: $first, after: $after, states: $states, orderBy: {field: $orderBy, direction: $direction}){nodes{number,title,body,state,databaseId,author{login},createdAt,updatedAt,labels(first: 100){nodes{name,id,description}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount}}}" - qWithLabels := "query($after:String$direction:OrderDirection!$first:Int!$labels:[String!]!$orderBy:IssueOrderField!$owner:String!$repo:String!$states:[IssueState!]!){repository(owner: $owner, name: $repo){issues(first: $first, after: $after, labels: $labels, states: $states, orderBy: {field: $orderBy, direction: $direction}){nodes{number,title,body,state,databaseId,author{login},createdAt,updatedAt,labels(first: 100){nodes{name,id,description}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount}}}" + qBasicNoLabels := "query($after:String$direction:OrderDirection!$first:Int!$orderBy:IssueOrderField!$owner:String!$repo:String!$states:[IssueState!]!){repository(owner: $owner, name: $repo){issues(first: $first, after: $after, states: $states, orderBy: {field: $orderBy, direction: $direction}){nodes{number,title,body,state,databaseId,author{login},createdAt,updatedAt,labels(first: 100){nodes{name,id,description}},comments{totalCount}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount}}}" + qWithLabels := "query($after:String$direction:OrderDirection!$first:Int!$labels:[String!]!$orderBy:IssueOrderField!$owner:String!$repo:String!$states:[IssueState!]!){repository(owner: $owner, name: $repo){issues(first: $first, after: $after, labels: $labels, states: $states, orderBy: {field: $orderBy, direction: $direction}){nodes{number,title,body,state,databaseId,author{login},createdAt,updatedAt,labels(first: 100){nodes{name,id,description}},comments{totalCount}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount}}}" for _, tc := range tests { t.Run(tc.name, func(t *testing.T) {