Skip to content

Commit 2f68b30

Browse files
committed
Using issues suggestions feature flag
1 parent 457f599 commit 2f68b30

2 files changed

Lines changed: 104 additions & 1 deletion

File tree

pkg/github/granular_tools_test.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88

99
"github.com/github/github-mcp-server/internal/githubv4mock"
1010
"github.com/github/github-mcp-server/internal/toolsnaps"
11+
"github.com/github/github-mcp-server/pkg/http/headers"
12+
transportpkg "github.com/github/github-mcp-server/pkg/http/transport"
1113
"github.com/github/github-mcp-server/pkg/inventory"
1214
"github.com/github/github-mcp-server/pkg/translations"
1315
gogithub "github.com/google/go-github/v87/github"
@@ -1802,4 +1804,101 @@ func TestGranularSetIssueFields(t *testing.T) {
18021804
require.NoError(t, err)
18031805
assert.False(t, result.IsError)
18041806
})
1807+
1808+
t.Run("sends GraphQL-Features: update_issue_suggestions header on mutation", func(t *testing.T) {
1809+
matchers := []githubv4mock.Matcher{
1810+
githubv4mock.NewQueryMatcher(
1811+
struct {
1812+
Repository struct {
1813+
Issue struct {
1814+
ID githubv4.ID
1815+
} `graphql:"issue(number: $issueNumber)"`
1816+
} `graphql:"repository(owner: $owner, name: $repo)"`
1817+
}{},
1818+
map[string]any{
1819+
"owner": githubv4.String("owner"),
1820+
"repo": githubv4.String("repo"),
1821+
"issueNumber": githubv4.Int(5),
1822+
},
1823+
githubv4mock.DataResponse(map[string]any{
1824+
"repository": map[string]any{
1825+
"issue": map[string]any{"id": "ISSUE_123"},
1826+
},
1827+
}),
1828+
),
1829+
githubv4mock.NewMutationMatcher(
1830+
struct {
1831+
SetIssueFieldValue struct {
1832+
Issue struct {
1833+
ID githubv4.ID
1834+
Number githubv4.Int
1835+
URL githubv4.String
1836+
}
1837+
IssueFieldValues []struct {
1838+
TextValue struct {
1839+
Value string
1840+
} `graphql:"... on IssueFieldTextValue"`
1841+
SingleSelectValue struct {
1842+
Name string
1843+
} `graphql:"... on IssueFieldSingleSelectValue"`
1844+
DateValue struct {
1845+
Value string
1846+
} `graphql:"... on IssueFieldDateValue"`
1847+
NumberValue struct {
1848+
Value float64
1849+
} `graphql:"... on IssueFieldNumberValue"`
1850+
}
1851+
} `graphql:"setIssueFieldValue(input: $input)"`
1852+
}{},
1853+
SetIssueFieldValueInput{
1854+
IssueID: githubv4.ID("ISSUE_123"),
1855+
IssueFields: []IssueFieldCreateOrUpdateInput{
1856+
{
1857+
FieldID: githubv4.ID("FIELD_1"),
1858+
TextValue: githubv4.NewString(githubv4.String("hello")),
1859+
},
1860+
},
1861+
},
1862+
nil,
1863+
githubv4mock.DataResponse(map[string]any{
1864+
"setIssueFieldValue": map[string]any{
1865+
"issue": map[string]any{
1866+
"id": "ISSUE_123",
1867+
"number": 5,
1868+
"url": "https://github.com/owner/repo/issues/5",
1869+
},
1870+
},
1871+
}),
1872+
),
1873+
}
1874+
1875+
// Build a transport chain matching production: GraphQLFeaturesTransport
1876+
// wraps a header-capturing spy, which forwards to the mock's RoundTripper.
1877+
// This verifies the mutation request sets the update_issue_suggestions
1878+
// feature flag so the rationale/suggest input fields are accepted.
1879+
mockClient := githubv4mock.NewMockedHTTPClient(matchers...)
1880+
spy := &headerCaptureTransport{inner: mockClient.Transport}
1881+
httpClient := &http.Client{
1882+
Transport: &transportpkg.GraphQLFeaturesTransport{Transport: spy},
1883+
}
1884+
gqlClient := githubv4.NewClient(httpClient)
1885+
deps := BaseDeps{GQLClient: gqlClient}
1886+
serverTool := GranularSetIssueFields(translations.NullTranslationHelper)
1887+
handler := serverTool.Handler(deps)
1888+
1889+
request := createMCPRequest(map[string]any{
1890+
"owner": "owner",
1891+
"repo": "repo",
1892+
"issue_number": float64(5),
1893+
"fields": []any{
1894+
map[string]any{"field_id": "FIELD_1", "text_value": "hello"},
1895+
},
1896+
})
1897+
result, err := handler(ContextWithDeps(context.Background(), deps), &request)
1898+
require.NoError(t, err)
1899+
require.False(t, result.IsError, getTextResult(t, result).Text)
1900+
// The last request captured is the mutation; the preceding issue ID
1901+
// query does not require the feature flag.
1902+
assert.Equal(t, "update_issue_suggestions", spy.captured.Get(headers.GraphQLFeaturesHeader))
1903+
})
18051904
}

pkg/github/issues_granular.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"maps"
88
"strings"
99

10+
ghcontext "github.com/github/github-mcp-server/pkg/context"
1011
ghErrors "github.com/github/github-mcp-server/pkg/errors"
1112
"github.com/github/github-mcp-server/pkg/inventory"
1213
"github.com/github/github-mcp-server/pkg/scopes"
@@ -1170,7 +1171,10 @@ func GranularSetIssueFields(t translations.TranslationHelperFunc) inventory.Serv
11701171
IssueFields: issueFields,
11711172
}
11721173

1173-
if err := gqlClient.Mutate(ctx, &mutation, mutationInput, nil); err != nil {
1174+
// The rationale and suggest input fields on IssueFieldCreateOrUpdateInput
1175+
// are gated behind the update_issue_suggestions GraphQL feature flag.
1176+
ctxWithFeatures := ghcontext.WithGraphQLFeatures(ctx, "update_issue_suggestions")
1177+
if err := gqlClient.Mutate(ctxWithFeatures, &mutation, mutationInput, nil); err != nil {
11741178
return ghErrors.NewGitHubGraphQLErrorResponse(ctx, "failed to set issue field values", err), nil, nil
11751179
}
11761180

0 commit comments

Comments
 (0)