Skip to content

Commit 2aea819

Browse files
test(http): cover authorization server override wiring
Verify the HTTP-only configuration surface, unchanged host-derived default, and explicit override propagation through OAuth metadata. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent b102004 commit 2aea819

5 files changed

Lines changed: 80 additions & 7 deletions

File tree

cmd/github-mcp-server/main_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/github/github-mcp-server/pkg/inventory"
1010
"github.com/google/jsonschema-go/jsonschema"
1111
"github.com/modelcontextprotocol/go-sdk/mcp"
12+
"github.com/spf13/viper"
1213
"github.com/stretchr/testify/assert"
1314
"github.com/stretchr/testify/require"
1415
)
@@ -41,6 +42,20 @@ func TestGitHubAppFlagsAreStdioOnly(t *testing.T) {
4142
assert.Nil(t, httpCmd.Flags().Lookup("app-id"))
4243
}
4344

45+
func TestAuthorizationServerConfigurationIsHTTPOnly(t *testing.T) {
46+
flag := httpCmd.Flags().Lookup("authorization-server")
47+
require.NotNil(t, flag)
48+
assert.Empty(t, flag.DefValue)
49+
assert.Nil(t, stdioCmd.Flags().Lookup("authorization-server"))
50+
51+
t.Setenv("GITHUB_AUTHORIZATION_SERVER", "")
52+
initConfig()
53+
assert.Empty(t, viper.GetString("authorization-server"))
54+
55+
t.Setenv("GITHUB_AUTHORIZATION_SERVER", "https://oauth-proxy.example.com")
56+
assert.Equal(t, "https://oauth-proxy.example.com", viper.GetString("authorization-server"))
57+
}
58+
4459
func TestWriteToolDocScopeSemantics(t *testing.T) {
4560
tests := []struct {
4661
name string

docs/streamable-http.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ The `authorization_servers` field in the protected resource metadata will then p
117117

118118
Equivalent environment variable: `GITHUB_AUTHORIZATION_SERVER=https://mcp.example.com/oauth-proxy`.
119119

120+
When neither the flag nor environment variable is set, the server preserves the existing behavior and derives the authorization server from `--gh-host`. The override only changes the URL advertised in OAuth protected resource metadata; it does not change token validation or the GitHub API host.
121+
120122
## Client Configuration
121123

122124
### Using OAuth Authentication

pkg/http/oauth/oauth_test.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package oauth
22

33
import (
4+
"context"
45
"crypto/tls"
56
"encoding/json"
67
"net/http"
78
"net/http/httptest"
9+
"net/url"
810
"testing"
911

1012
"github.com/github/github-mcp-server/pkg/http/headers"
@@ -18,6 +20,16 @@ var (
1820
defaultAuthorizationServer = "https://github.com/login/oauth"
1921
)
2022

23+
type countingAPIHostResolver struct {
24+
utils.APIHostResolver
25+
authorizationServerURLCalls int
26+
}
27+
28+
func (r *countingAPIHostResolver) AuthorizationServerURL(ctx context.Context) (*url.URL, error) {
29+
r.authorizationServerURLCalls++
30+
return r.APIHostResolver.AuthorizationServerURL(ctx)
31+
}
32+
2133
func TestNewAuthHandler(t *testing.T) {
2234
t.Parallel()
2335

@@ -729,14 +741,15 @@ func TestAPIHostResolver_AuthorizationServerURL(t *testing.T) {
729741
return
730742
}
731743
require.NoError(t, err)
744+
countingAPIHost := &countingAPIHostResolver{APIHostResolver: apiHost}
732745

733746
config := tc.oauthConfig
734747
if config == nil {
735748
config = &Config{}
736749
}
737750
config.BaseURL = tc.host
738751

739-
handler, err := NewAuthHandler(config, apiHost)
752+
handler, err := NewAuthHandler(config, countingAPIHost)
740753
require.NoError(t, err)
741754

742755
router := chi.NewRouter()
@@ -767,6 +780,11 @@ func TestAPIHostResolver_AuthorizationServerURL(t *testing.T) {
767780
require.True(t, ok)
768781
require.Len(t, responseAuthServers, 1)
769782
assert.Equal(t, tc.expectedURL, responseAuthServers[0])
783+
if config.AuthorizationServer == "" {
784+
assert.Equal(t, 1, countingAPIHost.authorizationServerURLCalls)
785+
} else {
786+
assert.Zero(t, countingAPIHost.authorizationServerURLCalls)
787+
}
770788
})
771789
}
772790
}

pkg/http/server.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,7 @@ func RunHTTPServer(cfg ServerConfig) error {
197197
}
198198

199199
// Register OAuth protected resource metadata endpoints
200-
oauthCfg := &oauth.Config{
201-
BaseURL: cfg.BaseURL,
202-
ResourcePath: cfg.ResourcePath,
203-
TrustProxyHeaders: cfg.TrustProxyHeaders,
204-
AuthorizationServer: cfg.AuthorizationServer,
205-
}
200+
oauthCfg := newOAuthConfig(cfg)
206201

207202
serverOptions := []HandlerOption{
208203
WithInventoryFactory(inventoryFactory),
@@ -264,6 +259,15 @@ func RunHTTPServer(cfg ServerConfig) error {
264259
return nil
265260
}
266261

262+
func newOAuthConfig(cfg ServerConfig) *oauth.Config {
263+
return &oauth.Config{
264+
BaseURL: cfg.BaseURL,
265+
ResourcePath: cfg.ResourcePath,
266+
TrustProxyHeaders: cfg.TrustProxyHeaders,
267+
AuthorizationServer: cfg.AuthorizationServer,
268+
}
269+
}
270+
267271
// resolveListenAddress returns the address string passed to http.Server.
268272
// When host is empty the server binds to all interfaces on the given port;
269273
// otherwise host and port are joined into a single address.

pkg/http/server_test.go

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

1010
ghcontext "github.com/github/github-mcp-server/pkg/context"
1111
"github.com/github/github-mcp-server/pkg/github"
12+
"github.com/github/github-mcp-server/pkg/http/oauth"
1213
"github.com/github/github-mcp-server/pkg/inventory"
1314
"github.com/github/github-mcp-server/pkg/utils"
1415
"github.com/stretchr/testify/assert"
@@ -44,6 +45,39 @@ func TestRunHTTPServerRejectsInvalidStaticTools(t *testing.T) {
4445
}
4546
}
4647

48+
func TestNewOAuthConfig(t *testing.T) {
49+
tests := []struct {
50+
name string
51+
authorizationServer string
52+
}{
53+
{
54+
name: "unset preserves host-derived authorization server",
55+
},
56+
{
57+
name: "explicit override is propagated",
58+
authorizationServer: "https://oauth-proxy.example.com",
59+
},
60+
}
61+
62+
for _, tt := range tests {
63+
t.Run(tt.name, func(t *testing.T) {
64+
cfg := newOAuthConfig(ServerConfig{
65+
BaseURL: "https://mcp.example.com",
66+
ResourcePath: "/mcp",
67+
TrustProxyHeaders: true,
68+
AuthorizationServer: tt.authorizationServer,
69+
})
70+
71+
assert.Equal(t, &oauth.Config{
72+
BaseURL: "https://mcp.example.com",
73+
ResourcePath: "/mcp",
74+
TrustProxyHeaders: true,
75+
AuthorizationServer: tt.authorizationServer,
76+
}, cfg)
77+
})
78+
}
79+
}
80+
4781
func TestInitGlobalToolScopeMapUsesHost(t *testing.T) {
4882
tests := []struct {
4983
name string

0 commit comments

Comments
 (0)