Remerge remote-tracking branch 'origin/main' into fix-790-separate-issues-from-prs

This commit is contained in:
Matt Babbage
2025-08-08 14:54:47 +01:00
45 changed files with 180 additions and 99 deletions
+2 -2
View File
@@ -15,7 +15,7 @@
"type": "string"
},
"owner": {
"description": "Optional repository owner. If provided with repo, only notifications for this repository are listed.",
"description": "Optional repository owner. If provided with repo, only issues for this repository are listed.",
"type": "string"
},
"page": {
@@ -34,7 +34,7 @@
"type": "string"
},
"repo": {
"description": "Optional repository name. If provided with owner, only notifications for this repository are listed.",
"description": "Optional repository name. If provided with owner, only issues for this repository are listed.",
"type": "string"
},
"sort": {
@@ -15,7 +15,7 @@
"type": "string"
},
"owner": {
"description": "Optional repository owner. If provided with repo, only notifications for this repository are listed.",
"description": "Optional repository owner. If provided with repo, only pull requests for this repository are listed.",
"type": "string"
},
"page": {
@@ -34,7 +34,7 @@
"type": "string"
},
"repo": {
"description": "Optional repository name. If provided with owner, only notifications for this repository are listed.",
"description": "Optional repository name. If provided with owner, only pull requests for this repository are listed.",
"type": "string"
},
"sort": {
+1 -1
View File
@@ -11,7 +11,7 @@ import (
ghErrors "github.com/github/github-mcp-server/pkg/errors"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"testing"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+1 -1
View File
@@ -9,7 +9,7 @@ import (
ghErrors "github.com/github/github-mcp-server/pkg/errors"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"github.com/github/github-mcp-server/internal/toolsnaps"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"github.com/github/github-mcp-server/internal/toolsnaps"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+1 -1
View File
@@ -9,7 +9,7 @@ import (
ghErrors "github.com/github/github-mcp-server/pkg/errors"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"github.com/github/github-mcp-server/internal/toolsnaps"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+16 -12
View File
@@ -7,7 +7,7 @@ import (
"github.com/github/github-mcp-server/pkg/translations"
"github.com/go-viper/mapstructure/v2"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"github.com/shurcooL/githubv4"
@@ -443,7 +443,7 @@ func GetDiscussionComments(getGQLClient GetGQLClientFn, t translations.Translati
func ListDiscussionCategories(getGQLClient GetGQLClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("list_discussion_categories",
mcp.WithDescription(t("TOOL_LIST_DISCUSSION_CATEGORIES_DESCRIPTION", "List discussion categories with their id and name, for a repository")),
mcp.WithDescription(t("TOOL_LIST_DISCUSSION_CATEGORIES_DESCRIPTION", "List discussion categories with their id and name, for a repository or organisation.")),
mcp.WithToolAnnotation(mcp.ToolAnnotation{
Title: t("TOOL_LIST_DISCUSSION_CATEGORIES_USER_TITLE", "List discussion categories"),
ReadOnlyHint: ToBoolPtr(true),
@@ -453,19 +453,23 @@ func ListDiscussionCategories(getGQLClient GetGQLClientFn, t translations.Transl
mcp.Description("Repository owner"),
),
mcp.WithString("repo",
mcp.Required(),
mcp.Description("Repository name"),
mcp.Description("Repository name. If not provided, discussion categories will be queried at the organisation level."),
),
),
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// Decode params
var params struct {
Owner string
Repo string
}
if err := mapstructure.Decode(request.Params.Arguments, &params); err != nil {
owner, err := RequiredParam[string](request, "owner")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
repo, err := OptionalParam[string](request, "repo")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
// when not provided, default to the .github repository
// this will query discussion categories at the organisation level
if repo == "" {
repo = ".github"
}
client, err := getGQLClient(ctx)
if err != nil {
@@ -490,8 +494,8 @@ func ListDiscussionCategories(getGQLClient GetGQLClientFn, t translations.Transl
} `graphql:"repository(owner: $owner, name: $repo)"`
}
vars := map[string]interface{}{
"owner": githubv4.String(params.Owner),
"repo": githubv4.String(params.Repo),
"owner": githubv4.String(owner),
"repo": githubv4.String(repo),
"first": githubv4.Int(25),
}
if err := client.Query(ctx, &q, vars); err != nil {
+111 -34
View File
@@ -9,7 +9,7 @@ import (
"github.com/github/github-mcp-server/internal/githubv4mock"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/shurcooL/githubv4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -638,17 +638,33 @@ func Test_GetDiscussionComments(t *testing.T) {
}
func Test_ListDiscussionCategories(t *testing.T) {
mockClient := githubv4.NewClient(nil)
toolDef, _ := ListDiscussionCategories(stubGetGQLClientFn(mockClient), translations.NullTranslationHelper)
assert.Equal(t, "list_discussion_categories", toolDef.Name)
assert.NotEmpty(t, toolDef.Description)
assert.Contains(t, toolDef.Description, "or organisation")
assert.Contains(t, toolDef.InputSchema.Properties, "owner")
assert.Contains(t, toolDef.InputSchema.Properties, "repo")
assert.ElementsMatch(t, toolDef.InputSchema.Required, []string{"owner"})
// Use exact string query that matches implementation output
qListCategories := "query($first:Int!$owner:String!$repo:String!){repository(owner: $owner, name: $repo){discussionCategories(first: $first){nodes{id,name},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount}}}"
// Variables matching what GraphQL receives after JSON marshaling/unmarshaling
vars := map[string]interface{}{
// Variables for repository-level categories
varsRepo := map[string]interface{}{
"owner": "owner",
"repo": "repo",
"first": float64(25),
}
mockResp := githubv4mock.DataResponse(map[string]any{
// Variables for organization-level categories (using .github repo)
varsOrg := map[string]interface{}{
"owner": "owner",
"repo": ".github",
"first": float64(25),
}
mockRespRepo := githubv4mock.DataResponse(map[string]any{
"repository": map[string]any{
"discussionCategories": map[string]any{
"nodes": []map[string]any{
@@ -665,37 +681,98 @@ func Test_ListDiscussionCategories(t *testing.T) {
},
},
})
matcher := githubv4mock.NewQueryMatcher(qListCategories, vars, mockResp)
httpClient := githubv4mock.NewMockedHTTPClient(matcher)
gqlClient := githubv4.NewClient(httpClient)
tool, handler := ListDiscussionCategories(stubGetGQLClientFn(gqlClient), translations.NullTranslationHelper)
assert.Equal(t, "list_discussion_categories", tool.Name)
assert.NotEmpty(t, tool.Description)
assert.Contains(t, tool.InputSchema.Properties, "owner")
assert.Contains(t, tool.InputSchema.Properties, "repo")
assert.ElementsMatch(t, tool.InputSchema.Required, []string{"owner", "repo"})
mockRespOrg := githubv4mock.DataResponse(map[string]any{
"repository": map[string]any{
"discussionCategories": map[string]any{
"nodes": []map[string]any{
{"id": "789", "name": "Announcements"},
{"id": "101", "name": "General"},
{"id": "112", "name": "Ideas"},
},
"pageInfo": map[string]any{
"hasNextPage": false,
"hasPreviousPage": false,
"startCursor": "",
"endCursor": "",
},
"totalCount": 3,
},
},
})
request := createMCPRequest(map[string]interface{}{"owner": "owner", "repo": "repo"})
result, err := handler(context.Background(), request)
require.NoError(t, err)
text := getTextResult(t, result).Text
var response struct {
Categories []map[string]string `json:"categories"`
PageInfo struct {
HasNextPage bool `json:"hasNextPage"`
HasPreviousPage bool `json:"hasPreviousPage"`
StartCursor string `json:"startCursor"`
EndCursor string `json:"endCursor"`
} `json:"pageInfo"`
TotalCount int `json:"totalCount"`
tests := []struct {
name string
reqParams map[string]interface{}
vars map[string]interface{}
mockResponse githubv4mock.GQLResponse
expectError bool
expectedCount int
expectedCategories []map[string]string
}{
{
name: "list repository-level discussion categories",
reqParams: map[string]interface{}{
"owner": "owner",
"repo": "repo",
},
vars: varsRepo,
mockResponse: mockRespRepo,
expectError: false,
expectedCount: 2,
expectedCategories: []map[string]string{
{"id": "123", "name": "CategoryOne"},
{"id": "456", "name": "CategoryTwo"},
},
},
{
name: "list org-level discussion categories (no repo provided)",
reqParams: map[string]interface{}{
"owner": "owner",
// repo is not provided, it will default to ".github"
},
vars: varsOrg,
mockResponse: mockRespOrg,
expectError: false,
expectedCount: 3,
expectedCategories: []map[string]string{
{"id": "789", "name": "Announcements"},
{"id": "101", "name": "General"},
{"id": "112", "name": "Ideas"},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
matcher := githubv4mock.NewQueryMatcher(qListCategories, tc.vars, tc.mockResponse)
httpClient := githubv4mock.NewMockedHTTPClient(matcher)
gqlClient := githubv4.NewClient(httpClient)
_, handler := ListDiscussionCategories(stubGetGQLClientFn(gqlClient), translations.NullTranslationHelper)
req := createMCPRequest(tc.reqParams)
res, err := handler(context.Background(), req)
text := getTextResult(t, res).Text
if tc.expectError {
require.True(t, res.IsError)
return
}
require.NoError(t, err)
var response struct {
Categories []map[string]string `json:"categories"`
PageInfo struct {
HasNextPage bool `json:"hasNextPage"`
HasPreviousPage bool `json:"hasPreviousPage"`
StartCursor string `json:"startCursor"`
EndCursor string `json:"endCursor"`
} `json:"pageInfo"`
TotalCount int `json:"totalCount"`
}
require.NoError(t, json.Unmarshal([]byte(text), &response))
assert.Equal(t, tc.expectedCategories, response.Categories)
})
}
require.NoError(t, json.Unmarshal([]byte(text), &response))
assert.Len(t, response.Categories, 2)
assert.Equal(t, "123", response.Categories[0]["id"])
assert.Equal(t, "CategoryOne", response.Categories[0]["name"])
assert.Equal(t, "456", response.Categories[1]["id"])
assert.Equal(t, "CategoryTwo", response.Categories[1]["name"])
}
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"net/http"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"time"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+3 -3
View File
@@ -12,7 +12,7 @@ import (
ghErrors "github.com/github/github-mcp-server/pkg/errors"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/go-viper/mapstructure/v2"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"github.com/shurcooL/githubv4"
@@ -691,10 +691,10 @@ func SearchIssues(getClient GetClientFn, t translations.TranslationHelperFunc) (
mcp.Description("Search query using GitHub issues search syntax"),
),
mcp.WithString("owner",
mcp.Description("Optional repository owner. If provided with repo, only notifications for this repository are listed."),
mcp.Description("Optional repository owner. If provided with repo, only issues for this repository are listed."),
),
mcp.WithString("repo",
mcp.Description("Optional repository name. If provided with owner, only notifications for this repository are listed."),
mcp.Description("Optional repository name. If provided with owner, only issues for this repository are listed."),
),
mcp.WithString("sort",
mcp.Description("Sort field by number of matches of categories, defaults to best match"),
+1 -1
View File
@@ -11,7 +11,7 @@ import (
"github.com/github/github-mcp-server/internal/githubv4mock"
"github.com/github/github-mcp-server/internal/toolsnaps"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/shurcooL/githubv4"
"github.com/stretchr/testify/assert"
+1 -1
View File
@@ -11,7 +11,7 @@ import (
ghErrors "github.com/github/github-mcp-server/pkg/errors"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"github.com/github/github-mcp-server/internal/toolsnaps"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+3 -3
View File
@@ -8,7 +8,7 @@ import (
"net/http"
"github.com/go-viper/mapstructure/v2"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"github.com/shurcooL/githubv4"
@@ -691,10 +691,10 @@ func SearchPullRequests(getClient GetClientFn, t translations.TranslationHelperF
mcp.Description("Search query using GitHub pull request search syntax"),
),
mcp.WithString("owner",
mcp.Description("Optional repository owner. If provided with repo, only notifications for this repository are listed."),
mcp.Description("Optional repository owner. If provided with repo, only pull requests for this repository are listed."),
),
mcp.WithString("repo",
mcp.Description("Optional repository name. If provided with owner, only notifications for this repository are listed."),
mcp.Description("Optional repository name. If provided with owner, only pull requests for this repository are listed."),
),
mcp.WithString("sort",
mcp.Description("Sort field by number of matches of categories, defaults to best match"),
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"github.com/github/github-mcp-server/internal/githubv4mock"
"github.com/github/github-mcp-server/internal/toolsnaps"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/shurcooL/githubv4"
"github.com/migueleliasweb/go-github-mock/src/mock"
+1 -1
View File
@@ -13,7 +13,7 @@ import (
ghErrors "github.com/github/github-mcp-server/pkg/errors"
"github.com/github/github-mcp-server/pkg/raw"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
+1 -1
View File
@@ -12,7 +12,7 @@ import (
"github.com/github/github-mcp-server/internal/toolsnaps"
"github.com/github/github-mcp-server/pkg/raw"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/stretchr/testify/assert"
+1 -1
View File
@@ -14,7 +14,7 @@ import (
"github.com/github/github-mcp-server/pkg/raw"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"github.com/github/github-mcp-server/pkg/raw"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/stretchr/testify/require"
+1 -1
View File
@@ -8,7 +8,7 @@ import (
ghErrors "github.com/github/github-mcp-server/pkg/errors"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"github.com/github/github-mcp-server/internal/toolsnaps"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"io"
"net/http"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/mcp"
)
+1 -1
View File
@@ -9,7 +9,7 @@ import (
ghErrors "github.com/github/github-mcp-server/pkg/errors"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"testing"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"errors"
"fmt"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"testing"
"github.com/github/github-mcp-server/pkg/raw"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/shurcooL/githubv4"
"github.com/stretchr/testify/assert"
)
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"github.com/github/github-mcp-server/pkg/raw"
"github.com/github/github-mcp-server/pkg/toolsets"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/server"
"github.com/shurcooL/githubv4"
)