Skip to content

Commit 6b28af5

Browse files
committed
Allow browser-based MCP clients via CORS and cross-origin bypass
1 parent 569a48d commit 6b28af5

File tree

2 files changed

+46
-1
lines changed

2 files changed

+46
-1
lines changed

pkg/http/handler.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ package http
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"log/slog"
78
"net/http"
89

910
ghcontext "github.com/github/github-mcp-server/pkg/context"
1011
"github.com/github/github-mcp-server/pkg/github"
12+
"github.com/github/github-mcp-server/pkg/http/headers"
1113
"github.com/github/github-mcp-server/pkg/http/middleware"
1214
"github.com/github/github-mcp-server/pkg/http/oauth"
1315
"github.com/github/github-mcp-server/pkg/inventory"
@@ -226,7 +228,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
226228
mcpHandler := mcp.NewStreamableHTTPHandler(func(_ *http.Request) *mcp.Server {
227229
return ghServer
228230
}, &mcp.StreamableHTTPOptions{
229-
Stateless: true,
231+
Stateless: true,
232+
CrossOriginProtection: h.config.CrossOriginProtection,
230233
})
231234

232235
mcpHandler.ServeHTTP(w, r)
@@ -412,3 +415,31 @@ func PATScopeFilter(b *inventory.Builder, r *http.Request, fetcher scopes.Fetche
412415

413416
return b
414417
}
418+
419+
// SetCorsHeaders is middleware that sets CORS headers to allow browser-based
420+
// MCP clients to connect from any origin. This is safe because the server
421+
// authenticates via bearer tokens (not cookies), so cross-origin requests
422+
// cannot exploit ambient credentials.
423+
func SetCorsHeaders(h http.Handler) http.Handler {
424+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
425+
w.Header().Set("Access-Control-Allow-Origin", "*")
426+
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS")
427+
w.Header().Set("Access-Control-Max-Age", "86400")
428+
w.Header().Set("Access-Control-Expose-Headers", "Mcp-Session-Id")
429+
w.Header().Set("Access-Control-Allow-Headers", fmt.Sprintf(
430+
"Content-Type, Authorization, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID, %s, %s, %s, %s, %s, %s",
431+
headers.MCPReadOnlyHeader,
432+
headers.MCPToolsetsHeader,
433+
headers.MCPToolsHeader,
434+
headers.MCPExcludeToolsHeader,
435+
headers.MCPFeaturesHeader,
436+
headers.AuthorizationHeader,
437+
))
438+
439+
if r.Method == http.MethodOptions {
440+
w.WriteHeader(http.StatusOK)
441+
return
442+
}
443+
h.ServeHTTP(w, r)
444+
})
445+
}

pkg/http/server.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ type ServerConfig struct {
8686

8787
// InsidersMode indicates if we should enable experimental features.
8888
InsidersMode bool
89+
90+
// CrossOriginProtection configures the SDK's cross-origin request protection.
91+
// If nil, the SDK default (reject cross-origin POSTs) is used.
92+
CrossOriginProtection *http.CrossOriginProtection
8993
}
9094

9195
func RunHTTPServer(cfg ServerConfig) error {
@@ -159,6 +163,14 @@ func RunHTTPServer(cfg ServerConfig) error {
159163
serverOptions = append(serverOptions, WithScopeFetcher(scopeFetcher))
160164
}
161165

166+
// Bypass cross-origin protection: this server uses bearer tokens, not
167+
// cookies, so CSRF checks are unnecessary.
168+
if cfg.CrossOriginProtection == nil {
169+
p := http.NewCrossOriginProtection()
170+
p.AddInsecureBypassPattern("/")
171+
cfg.CrossOriginProtection = p
172+
}
173+
162174
r := chi.NewRouter()
163175
handler := NewHTTPMcpHandler(ctx, &cfg, deps, t, logger, apiHost, append(serverOptions, WithFeatureChecker(featureChecker), WithOAuthConfig(oauthCfg))...)
164176
oauthHandler, err := oauth.NewAuthHandler(oauthCfg, apiHost)
@@ -167,6 +179,8 @@ func RunHTTPServer(cfg ServerConfig) error {
167179
}
168180

169181
r.Group(func(r chi.Router) {
182+
r.Use(SetCorsHeaders)
183+
170184
// Register Middleware First, needs to be before route registration
171185
handler.RegisterMiddleware(r)
172186

0 commit comments

Comments
 (0)