Skip to content

Commit d124c6b

Browse files
committed
Migrate to GitHub REST API version 2026-03-10
1 parent eb4c099 commit d124c6b

11 files changed

Lines changed: 132 additions & 5 deletions

File tree

internal/ghmcp/oauth_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,8 +576,10 @@ func TestCreateGitHubClientsTokenProvider(t *testing.T) {
576576
t.Parallel()
577577

578578
var gotAuth string
579+
var gotAPIVersion string
579580
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
580581
gotAuth = r.Header.Get(headers.AuthorizationHeader)
582+
gotAPIVersion = r.Header.Get(headers.GitHubAPIVersionHeader)
581583
w.WriteHeader(http.StatusOK)
582584
}))
583585
defer server.Close()
@@ -600,6 +602,7 @@ func TestCreateGitHubClientsTokenProvider(t *testing.T) {
600602

601603
do()
602604
assert.Equal(t, "", gotAuth, "no auth header before authorization")
605+
assert.Equal(t, headers.GitHubAPIVersion, gotAPIVersion)
603606

604607
current = "oauth-token"
605608
do()

internal/ghmcp/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func createGitHubClients(cfg github.MCPServerConfig, apiHost utils.APIHostResolv
6767
// the latter installs its own round tripper that would pin the static token
6868
// and shadow the dynamic one.
6969
restUATransport := &transport.UserAgentTransport{
70-
Transport: http.DefaultTransport,
70+
Transport: &transport.APIVersionTransport{Transport: http.DefaultTransport},
7171
Agent: fmt.Sprintf("github-mcp-server/%s", cfg.Version),
7272
}
7373
var restClient *gogithub.Client

internal/githubapp/githubapp.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"sync"
2222
"time"
2323

24+
"github.com/github/github-mcp-server/pkg/http/headers"
2425
"golang.org/x/oauth2"
2526
)
2627

@@ -140,9 +141,9 @@ func (s *installationTokenSource) Token() (*oauth2.Token, error) {
140141
if err != nil {
141142
return nil, fmt.Errorf("creating installation token request: %w", err)
142143
}
143-
req.Header.Set("Authorization", "Bearer "+jwt)
144-
req.Header.Set("Accept", "application/vnd.github+json")
145-
req.Header.Set("X-GitHub-Api-Version", "2022-11-28")
144+
req.Header.Set(headers.AuthorizationHeader, "Bearer "+jwt)
145+
req.Header.Set(headers.AcceptHeader, "application/vnd.github+json")
146+
req.Header.Set(headers.GitHubAPIVersionHeader, headers.GitHubAPIVersion)
146147

147148
resp, err := s.httpClient.Do(req)
148149
if err != nil {

internal/githubapp/githubapp_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"testing"
2121
"time"
2222

23+
"github.com/github/github-mcp-server/pkg/http/headers"
2324
"github.com/stretchr/testify/assert"
2425
"github.com/stretchr/testify/require"
2526
)
@@ -155,6 +156,7 @@ func installationServer(t *testing.T, pub *rsa.PublicKey, token string, expiresA
155156
calls.Add(1)
156157
assert.Equal(t, http.MethodPost, r.Method)
157158
assert.Equal(t, "/app/installations/456/access_tokens", r.URL.Path)
159+
assert.Equal(t, headers.GitHubAPIVersion, r.Header.Get(headers.GitHubAPIVersionHeader))
158160

159161
authz := r.Header.Get("Authorization")
160162
require.True(t, strings.HasPrefix(authz, "Bearer "), "must send the app JWT as a bearer token")

pkg/github/dependencies.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,9 @@ func (d *RequestDeps) GetClient(ctx context.Context) (*gogithub.Client, error) {
324324

325325
// Construct REST client
326326
restClient, err := gogithub.NewClient(
327+
gogithub.WithHTTPClient(&http.Client{
328+
Transport: &transport.APIVersionTransport{Transport: http.DefaultTransport},
329+
}),
327330
gogithub.WithAuthToken(token),
328331
gogithub.WithUserAgent(fmt.Sprintf("github-mcp-server/%s", d.version)),
329332
gogithub.WithEnterpriseURLs(baseRestURL.String(), uploadURL.String()),

pkg/github/dependencies_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,65 @@ import (
44
"context"
55
"errors"
66
"log/slog"
7+
"net/http"
8+
"net/http/httptest"
9+
"net/url"
710
"testing"
811

12+
ghcontext "github.com/github/github-mcp-server/pkg/context"
913
"github.com/github/github-mcp-server/pkg/github"
14+
"github.com/github/github-mcp-server/pkg/http/headers"
1015
"github.com/github/github-mcp-server/pkg/observability"
1116
"github.com/github/github-mcp-server/pkg/observability/metrics"
1217
"github.com/github/github-mcp-server/pkg/translations"
1318
"github.com/stretchr/testify/assert"
19+
"github.com/stretchr/testify/require"
1420
)
1521

22+
type requestDepsAPIHost struct {
23+
url *url.URL
24+
}
25+
26+
func (h requestDepsAPIHost) BaseRESTURL(context.Context) (*url.URL, error) { return h.url, nil }
27+
func (h requestDepsAPIHost) GraphqlURL(context.Context) (*url.URL, error) { return h.url, nil }
28+
func (h requestDepsAPIHost) UploadURL(context.Context) (*url.URL, error) { return h.url, nil }
29+
func (h requestDepsAPIHost) RawURL(context.Context) (*url.URL, error) { return h.url, nil }
30+
func (h requestDepsAPIHost) AuthorizationServerURL(context.Context) (*url.URL, error) {
31+
return h.url, nil
32+
}
33+
1634
func testExporters() observability.Exporters {
1735
obs, _ := observability.NewExporters(slog.New(slog.DiscardHandler), metrics.NewNoopMetrics())
1836
return obs
1937
}
2038

39+
func TestRequestDepsGetClientSetsAPIVersion(t *testing.T) {
40+
t.Parallel()
41+
42+
var gotVersion string
43+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
44+
gotVersion = r.Header.Get(headers.GitHubAPIVersionHeader)
45+
w.WriteHeader(http.StatusOK)
46+
}))
47+
defer server.Close()
48+
49+
serverURL, err := url.Parse(server.URL)
50+
require.NoError(t, err)
51+
apiHost := requestDepsAPIHost{url: serverURL}
52+
deps := github.NewRequestDeps(apiHost, "test", false, nil, nil, 0, nil, testExporters())
53+
ctx := ghcontext.WithTokenInfo(context.Background(), &ghcontext.TokenInfo{Token: "test-token"})
54+
client, err := deps.GetClient(ctx)
55+
require.NoError(t, err)
56+
57+
req, err := client.NewRequest(ctx, http.MethodGet, "rate_limit", nil)
58+
require.NoError(t, err)
59+
resp, err := client.Do(req, nil)
60+
require.NoError(t, err)
61+
defer resp.Body.Close()
62+
63+
assert.Equal(t, headers.GitHubAPIVersion, gotVersion)
64+
}
65+
2166
func TestIsFeatureEnabled_WithEnabledFlag(t *testing.T) {
2267
t.Parallel()
2368

pkg/http/headers/headers.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,6 @@ const (
5353
GraphQLFeaturesHeader = "GraphQL-Features"
5454
// GitHubAPIVersionHeader is the header used to specify the GitHub API version.
5555
GitHubAPIVersionHeader = "X-GitHub-Api-Version"
56+
// GitHubAPIVersion is the GitHub REST API version used by this server.
57+
GitHubAPIVersion = "2026-03-10"
5658
)

pkg/http/transport/api_version.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package transport
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/github/github-mcp-server/pkg/http/headers"
7+
)
8+
9+
// APIVersionTransport sets the GitHub REST API version on every request.
10+
type APIVersionTransport struct {
11+
Transport http.RoundTripper
12+
}
13+
14+
// RoundTrip implements http.RoundTripper.
15+
func (t *APIVersionTransport) RoundTrip(req *http.Request) (*http.Response, error) {
16+
underlying := t.Transport
17+
if underlying == nil {
18+
underlying = http.DefaultTransport
19+
}
20+
21+
req = req.Clone(req.Context())
22+
req.Header.Set(headers.GitHubAPIVersionHeader, headers.GitHubAPIVersion)
23+
return underlying.RoundTrip(req)
24+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package transport
2+
3+
import (
4+
"net/http"
5+
"net/http/httptest"
6+
"testing"
7+
8+
"github.com/github/github-mcp-server/pkg/http/headers"
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
func TestAPIVersionTransport(t *testing.T) {
14+
t.Parallel()
15+
16+
var gotVersion string
17+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
18+
gotVersion = r.Header.Get(headers.GitHubAPIVersionHeader)
19+
w.WriteHeader(http.StatusOK)
20+
}))
21+
defer server.Close()
22+
23+
req, err := http.NewRequest(http.MethodGet, server.URL, nil)
24+
require.NoError(t, err)
25+
req.Header.Set(headers.GitHubAPIVersionHeader, "2022-11-28")
26+
27+
resp, err := (&APIVersionTransport{}).RoundTrip(req)
28+
require.NoError(t, err)
29+
defer resp.Body.Close()
30+
31+
assert.Equal(t, headers.GitHubAPIVersion, gotVersion)
32+
assert.Equal(t, "2022-11-28", req.Header.Get(headers.GitHubAPIVersionHeader))
33+
}

pkg/scopes/fetcher.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func (f *Fetcher) FetchTokenScopes(ctx context.Context, token string) ([]string,
8181

8282
req.Header.Set(headers.AuthorizationHeader, "Bearer "+token)
8383
req.Header.Set(headers.AcceptHeader, "application/vnd.github+json")
84-
req.Header.Set(headers.GitHubAPIVersionHeader, "2022-11-28")
84+
req.Header.Set(headers.GitHubAPIVersionHeader, headers.GitHubAPIVersion)
8585

8686
resp, err := f.client.Do(req)
8787
if err != nil {

0 commit comments

Comments
 (0)