From 9cbb564cd9acce1801e9060637fdceca1c2a8be4 Mon Sep 17 00:00:00 2001 From: Mathias Bergqvist Date: Wed, 6 May 2026 17:19:14 +0200 Subject: [PATCH] fix: add OAuth discovery endpoints for claude.ai handshake MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements RFC 9728 protected resource metadata and HEAD probe so claude.ai can complete its pre-handshake discovery without hitting 404. - GET /.well-known/oauth-protected-resource → 200 {"authorization_servers":[]} - GET /.well-known/oauth-authorization-server → 404 (no auth server) - HEAD /mcp → 200 + MCP-Protocol-Version: 2025-06-18 header Closes #2 Co-Authored-By: Claude Sonnet 4.6 --- cmd/gitea-mcp/main.go | 12 ++++++++++++ internal/mcp/server.go | 3 +++ internal/mcp/server_test.go | 9 +++++++++ 3 files changed, 24 insertions(+) diff --git a/cmd/gitea-mcp/main.go b/cmd/gitea-mcp/main.go index 6beb226..a942bc6 100644 --- a/cmd/gitea-mcp/main.go +++ b/cmd/gitea-mcp/main.go @@ -54,6 +54,18 @@ func main() { w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("ok")) }) + mux.HandleFunc("/.well-known/oauth-protected-resource", func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + return + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"authorization_servers":[]}`)) + }) + mux.HandleFunc("/.well-known/oauth-authorization-server", func(w http.ResponseWriter, r *http.Request) { + http.NotFound(w, r) + }) addr := ":" + cfg.Port logger.Info("gitea-mcp starting", "addr", addr, "version", "0.1.0") diff --git a/internal/mcp/server.go b/internal/mcp/server.go index 613d7c6..ff2dc93 100644 --- a/internal/mcp/server.go +++ b/internal/mcp/server.go @@ -31,6 +31,9 @@ func NewServer(opts ServerOptions) *Server { func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch r.Method { + case http.MethodHead: + w.Header().Set("MCP-Protocol-Version", ProtocolVersion) + w.WriteHeader(http.StatusOK) case http.MethodGet: s.handleGET(w, r) case http.MethodPost: diff --git a/internal/mcp/server_test.go b/internal/mcp/server_test.go index 441cd06..7088d98 100644 --- a/internal/mcp/server_test.go +++ b/internal/mcp/server_test.go @@ -118,6 +118,15 @@ func TestPostBodyTooLarge(t *testing.T) { assert.Equal(t, http.StatusBadRequest, rr.Code) } +func TestHEADReturnsMCPProtocolVersionHeader(t *testing.T) { + srv := newServer(t) + req := httptest.NewRequest(http.MethodHead, "/mcp", nil) + rr := httptest.NewRecorder() + srv.ServeHTTP(rr, req) + require.Equal(t, http.StatusOK, rr.Code) + assert.Equal(t, mcp.ProtocolVersion, rr.Header().Get("MCP-Protocol-Version")) +} + func TestToolsCallToolNotFound(t *testing.T) { srv := newServer(t) // Initialize to get a session ID.