fix(mcp): allow unauthenticated HTTP transport when token is unset

Treat an empty MCP_AUTH_TOKEN as local development mode and pass requests through to /mcp. Add tests for the no-token path and update the empty-token unit case accordingly.

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-02 07:35:35 +00:00
parent 7b22fd3141
commit 6adf61e593
2 changed files with 43 additions and 7 deletions

View file

@ -82,12 +82,11 @@ func (s *Service) ServeHTTP(ctx context.Context, addr string) error {
}
// withAuth wraps an http.Handler with Bearer token authentication.
// If token is empty, requests are rejected.
// If token is empty, authentication is disabled for local development.
func withAuth(token string, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.TrimSpace(token) == "" {
w.Header().Set("WWW-Authenticate", `Bearer`)
http.Error(w, `{"error":"authentication not configured"}`, http.StatusUnauthorized)
next.ServeHTTP(w, r)
return
}

View file

@ -107,6 +107,44 @@ func TestServeHTTP_Good_AuthRequired(t *testing.T) {
<-errCh
}
func TestServeHTTP_Good_NoAuthConfigured(t *testing.T) {
os.Unsetenv("MCP_AUTH_TOKEN")
s, err := New(Options{})
if err != nil {
t.Fatalf("Failed to create service: %v", err)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("Failed to find free port: %v", err)
}
addr := listener.Addr().String()
listener.Close()
errCh := make(chan error, 1)
go func() {
errCh <- s.ServeHTTP(ctx, addr)
}()
time.Sleep(100 * time.Millisecond)
resp, err := http.Get(fmt.Sprintf("http://%s/mcp", addr))
if err != nil {
t.Fatalf("request failed: %v", err)
}
resp.Body.Close()
if resp.StatusCode == 401 {
t.Fatalf("expected /mcp to be open without MCP_AUTH_TOKEN, got %d", resp.StatusCode)
}
cancel()
<-errCh
}
func TestWithAuth_Good_ValidToken(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
@ -157,19 +195,18 @@ func TestWithAuth_Bad_MissingToken(t *testing.T) {
}
}
func TestWithAuth_Bad_EmptyConfiguredToken(t *testing.T) {
func TestWithAuth_Good_EmptyConfiguredToken_DisablesAuth(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
})
// Empty token now requires explicit configuration
wrapped := withAuth("", handler)
req, _ := http.NewRequest("GET", "/", nil)
rr := &fakeResponseWriter{code: 200}
wrapped.ServeHTTP(rr, req)
if rr.code != 401 {
t.Errorf("expected 401 with empty configured token, got %d", rr.code)
if rr.code != 200 {
t.Errorf("expected 200 with empty configured token, got %d", rr.code)
}
}