From a618a9d9b07b9664f2e0f9030c6381bf2a462026 Mon Sep 17 00:00:00 2001 From: ppoffice <8849362+ppoffice@users.noreply.github.com> Date: Sun, 30 Aug 2026 15:40:34 -0700 Subject: [PATCH 1/2] Fix e2e harness compilation against go-github v89 and go-sdk v1.7 The e2e test package no longer compiled under --tags e2e because it lagged behind two dependency migrations: - go-github v89: NewClient now returns (*Client, error) and WithEnterpriseURLs moved from a *Client method to a ClientOptionsFunc. Update getRESTClient and the inline cleanup call sites, and drop the trailing nil options arg from ListReviewers. - go-sdk v1.7: ghmcp.NewMCPServer(MCPServerConfig) was replaced by ghmcp.NewStdioMCPServer(ctx, github.MCPServerConfig). Update the in-process setupMCPClient branch accordingly, supplying the required Logger and Version fields. --- e2e/e2e_test.go | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index 82075ec55c..80903325f3 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -6,6 +6,7 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "net/http" "os" "os/exec" @@ -66,15 +67,14 @@ func getRESTClient(t *testing.T) *gogithub.Client { token := getE2EToken(t) // Create a new GitHub client with the token - ghClient := gogithub.NewClient(nil).WithAuthToken(token) - + opts := []gogithub.ClientOptionsFunc{gogithub.WithAuthToken(token)} if host := getE2EHost(); host != "" && host != "https://github.com" { - var err error // Currently this works for GHEC because the API is exposed at the api subdomain and the path prefix // but it would be preferable to extract the host parsing from the main server logic, and use it here. - ghClient, err = ghClient.WithEnterpriseURLs(host, host) - require.NoError(t, err, "expected to create GitHub client with host") + opts = append(opts, gogithub.WithEnterpriseURLs(host, host)) } + ghClient, err := gogithub.NewClient(opts...) + require.NoError(t, err, "expected to create GitHub client successfully") return ghClient } @@ -221,11 +221,13 @@ func setupMCPClient(t *testing.T, options ...clientOption) *mcp.ClientSession { enabledToolsets = github.GetDefaultToolsetIDs() } - ghServer, err := ghmcp.NewMCPServer(ghmcp.MCPServerConfig{ + ghServer, err := ghmcp.NewStdioMCPServer(ctx, github.MCPServerConfig{ + Version: "e2e-test", Token: token, EnabledToolsets: enabledToolsets, Host: getE2EHost(), Translator: translations.NullTranslationHelper, + Logger: slog.New(slog.DiscardHandler), }) require.NoError(t, err, "expected to construct MCP server successfully") @@ -968,9 +970,10 @@ func TestRequestCopilotReview(t *testing.T) { // Cleanup the repository after the test t.Cleanup(func() { // MCP Server doesn't support deletions, but we can use the GitHub Client - ghClient := gogithub.NewClient(nil).WithAuthToken(getE2EToken(t)) + ghClient, err := gogithub.NewClient(gogithub.WithAuthToken(getE2EToken(t))) + require.NoError(t, err, "expected to create GitHub client successfully") t.Logf("Deleting repository %s/%s...", currentOwner, repoName) - _, err := ghClient.Repositories.Delete(context.Background(), currentOwner, repoName) + _, err = ghClient.Repositories.Delete(context.Background(), currentOwner, repoName) require.NoError(t, err, "expected to delete repository successfully") }) @@ -1063,9 +1066,10 @@ func TestRequestCopilotReview(t *testing.T) { // Finally, get requested reviews and see copilot is in there // MCP Server doesn't support requesting reviews yet, but we can use the GitHub Client - ghClient := gogithub.NewClient(nil).WithAuthToken(getE2EToken(t)) + ghClient, err := gogithub.NewClient(gogithub.WithAuthToken(getE2EToken(t))) + require.NoError(t, err, "expected to create GitHub client successfully") t.Logf("Getting reviews for pull request in %s/%s...", currentOwner, repoName) - reviewRequests, _, err := ghClient.PullRequests.ListReviewers(context.Background(), currentOwner, repoName, 1, nil) + reviewRequests, _, err := ghClient.PullRequests.ListReviewers(context.Background(), currentOwner, repoName, 1) require.NoError(t, err, "expected to get review requests successfully") // Check if Copilot was added as a reviewer - skip if not available From ef4fbd508d9aad170824e1f5b504574b03360a62 Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Tue, 1 Sep 2026 10:20:57 +0200 Subject: [PATCH 2/2] fix(e2e): align client host resolution Reuse the server API host resolver so GHEC REST and upload clients target the correct subdomains. Add token-free coverage for host URL resolution and the in-process stdio server lifecycle. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- e2e/e2e_test.go | 127 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 111 insertions(+), 16 deletions(-) diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index 80903325f3..112f653a37 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -19,6 +19,7 @@ import ( "github.com/github/github-mcp-server/internal/ghmcp" "github.com/github/github-mcp-server/pkg/github" "github.com/github/github-mcp-server/pkg/translations" + "github.com/github/github-mcp-server/pkg/utils" gogithub "github.com/google/go-github/v89/github" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/stretchr/testify/require" @@ -63,22 +64,118 @@ func getE2EHost() string { } func getRESTClient(t *testing.T) *gogithub.Client { - // Get token and ensure Docker image is built - token := getE2EToken(t) - - // Create a new GitHub client with the token - opts := []gogithub.ClientOptionsFunc{gogithub.WithAuthToken(token)} - if host := getE2EHost(); host != "" && host != "https://github.com" { - // Currently this works for GHEC because the API is exposed at the api subdomain and the path prefix - // but it would be preferable to extract the host parsing from the main server logic, and use it here. - opts = append(opts, gogithub.WithEnterpriseURLs(host, host)) - } - ghClient, err := gogithub.NewClient(opts...) + ghClient, err := newRESTClient(getE2EToken(t), getE2EHost()) require.NoError(t, err, "expected to create GitHub client successfully") return ghClient } +func newRESTClient(token, host string) (*gogithub.Client, error) { + apiHost, err := utils.NewAPIHost(host) + if err != nil { + return nil, fmt.Errorf("failed to parse API host: %w", err) + } + + restURL, err := apiHost.BaseRESTURL(context.Background()) + if err != nil { + return nil, fmt.Errorf("failed to get base REST URL: %w", err) + } + + uploadURL, err := apiHost.UploadURL(context.Background()) + if err != nil { + return nil, fmt.Errorf("failed to get upload URL: %w", err) + } + + return gogithub.NewClient( + gogithub.WithAuthToken(token), + gogithub.WithEnterpriseURLs(restURL.String(), uploadURL.String()), + ) +} + +func TestRESTClientURLs(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + host string + wantBaseURL string + wantUploadURL string + }{ + { + name: "dotcom default", + wantBaseURL: "https://api.github.com/", + wantUploadURL: "https://uploads.github.com/", + }, + { + name: "dotcom explicit", + host: "https://github.com", + wantBaseURL: "https://api.github.com/", + wantUploadURL: "https://uploads.github.com/", + }, + { + name: "GHEC", + host: "https://example.ghe.com", + wantBaseURL: "https://api.example.ghe.com/", + wantUploadURL: "https://uploads.example.ghe.com/", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + client, err := newRESTClient("test-token", tt.host) + require.NoError(t, err) + require.Equal(t, tt.wantBaseURL, client.BaseURL()) + require.Equal(t, tt.wantUploadURL, client.UploadURL()) + }) + } +} + +func TestInProcessStdioServer(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + server, err := ghmcp.NewStdioMCPServer(ctx, github.MCPServerConfig{ + Version: "e2e-test", + Token: "test-token", + EnabledToolsets: []string{"context"}, + Translator: translations.NullTranslationHelper, + Logger: slog.New(slog.DiscardHandler), + }) + require.NoError(t, err) + + serverTransport, clientTransport := mcp.NewInMemoryTransports() + serverErr := make(chan error, 1) + go func() { + serverErr <- server.Run(ctx, serverTransport) + }() + + client := mcp.NewClient(&mcp.Implementation{ + Name: "e2e-test-client", + Version: "0.0.1", + }, nil) + session, err := client.Connect(ctx, clientTransport, nil) + require.NoError(t, err) + t.Cleanup(func() { _ = session.Close() }) + + tools, err := session.ListTools(ctx, nil) + require.NoError(t, err) + require.True(t, slices.ContainsFunc(tools.Tools, func(tool *mcp.Tool) bool { + return tool.Name == "get_me" + })) + + require.NoError(t, session.Close()) + select { + case err := <-serverErr: + require.NoError(t, err) + case <-time.After(time.Second): + t.Fatal("timed out waiting for the in-process MCP server to stop") + } +} + // waitForRateLimit checks the current rate limit and waits if necessary. // It ensures we have at least minRateLimitRemaining requests available before proceeding. func waitForRateLimit(t *testing.T) { @@ -970,10 +1067,9 @@ func TestRequestCopilotReview(t *testing.T) { // Cleanup the repository after the test t.Cleanup(func() { // MCP Server doesn't support deletions, but we can use the GitHub Client - ghClient, err := gogithub.NewClient(gogithub.WithAuthToken(getE2EToken(t))) - require.NoError(t, err, "expected to create GitHub client successfully") + ghClient := getRESTClient(t) t.Logf("Deleting repository %s/%s...", currentOwner, repoName) - _, err = ghClient.Repositories.Delete(context.Background(), currentOwner, repoName) + _, err := ghClient.Repositories.Delete(context.Background(), currentOwner, repoName) require.NoError(t, err, "expected to delete repository successfully") }) @@ -1066,8 +1162,7 @@ func TestRequestCopilotReview(t *testing.T) { // Finally, get requested reviews and see copilot is in there // MCP Server doesn't support requesting reviews yet, but we can use the GitHub Client - ghClient, err := gogithub.NewClient(gogithub.WithAuthToken(getE2EToken(t))) - require.NoError(t, err, "expected to create GitHub client successfully") + ghClient := getRESTClient(t) t.Logf("Getting reviews for pull request in %s/%s...", currentOwner, repoName) reviewRequests, _, err := ghClient.PullRequests.ListReviewers(context.Background(), currentOwner, repoName, 1) require.NoError(t, err, "expected to get review requests successfully")