fix: add OAuth discovery endpoints so claude.ai connector can complete handshake #2

Closed
opened 2026-05-06 15:14:00 +00:00 by gitea-mcp-bot · 0 comments
Contributor

Context

claude.ai's custom connector flow now sends discovery requests to /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server before attempting the MCP initialize handshake. Both currently return 404, causing the connector to fail with "Couldn't reach the MCP server" before any MCP traffic is sent.

This is a claude.ai-side behaviour change — the connector was working previously without these endpoints. oauth2-proxy is not the fix; it was deliberately removed because Anthropic's backend performs server-to-server calls that cannot follow browser OAuth redirects.

The correct fix is to implement the OAuth protected resource metadata endpoint (RFC 9728 / MCP spec 2025-06-18) returning an empty authorization_servers array, which signals to claude.ai that the resource exists but requires no OAuth.

Verified via:

curl -si https://git-mcp.d-ma.be/.well-known/oauth-protected-resource
# → 404 (broken)
curl -si https://git-mcp.d-ma.be/.well-known/oauth-authorization-server  
# → 404 (broken)

Task

Add two well-known discovery endpoint handlers to main.go and add a HEAD handler to server.go that returns the required MCP-Protocol-Version header.

Implementation

1. main.go — add well-known routes

Register before the MCP handler:

mux.HandleFunc("/.well-known/oauth-protected-resource", func(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    _, _ = w.Write([]byte(`{"resource":"https://git-mcp.d-ma.be","bearer_methods_supported":[],"authorization_servers":[]}`))
})

mux.HandleFunc("/.well-known/oauth-authorization-server", func(w http.ResponseWriter, r *http.Request) {
    http.NotFound(w, r)
})

The empty authorization_servers array in the protected-resource response is the signal to clients that this resource requires no OAuth. The authorization-server endpoint returning 404 is consistent with that — there is no AS.

2. internal/mcp/server.go — add HEAD handler

claude.ai also sends HEAD /mcp to discover the protocol version before initialize. Currently returns 405. Add to ServeHTTP:

case http.MethodHead:
    w.Header().Set("MCP-Protocol-Version", ProtocolVersion)
    w.WriteHeader(http.StatusOK)

3. Verify after deploy

# Should return 200 with JSON body
curl -si https://git-mcp.d-ma.be/.well-known/oauth-protected-resource

# Should return 200 with MCP-Protocol-Version header
curl -I https://git-mcp.d-ma.be/mcp

# Full initialize handshake should still work
curl -s -X POST https://git-mcp.d-ma.be/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","method":"initialize","id":1,"params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"claude.ai","version":"1.0"}}}' | jq .

Acceptance criteria

  • GET /.well-known/oauth-protected-resource returns 200 with valid JSON containing authorization_servers: []
  • HEAD /mcp returns 200 with MCP-Protocol-Version: 2025-06-18 header
  • POST /mcp initialize handshake continues to work as before
  • claude.ai custom connector reconnects successfully after deploy

Branch

fix/oauth-discovery-endpoints from main

Notes

  • Do not add oauth2-proxy back. It breaks server-to-server MCP calls.
  • The resource field in the protected-resource response must match the public URL exactly.
  • No new dependencies needed — stdlib only.

Created via git-mcp on behalf of @mathiasbq

## Context claude.ai's custom connector flow now sends discovery requests to `/.well-known/oauth-protected-resource` and `/.well-known/oauth-authorization-server` before attempting the MCP `initialize` handshake. Both currently return 404, causing the connector to fail with "Couldn't reach the MCP server" before any MCP traffic is sent. This is a claude.ai-side behaviour change — the connector was working previously without these endpoints. oauth2-proxy is **not** the fix; it was deliberately removed because Anthropic's backend performs server-to-server calls that cannot follow browser OAuth redirects. The correct fix is to implement the OAuth protected resource metadata endpoint (RFC 9728 / MCP spec 2025-06-18) returning an empty `authorization_servers` array, which signals to claude.ai that the resource exists but requires no OAuth. Verified via: ```bash curl -si https://git-mcp.d-ma.be/.well-known/oauth-protected-resource # → 404 (broken) curl -si https://git-mcp.d-ma.be/.well-known/oauth-authorization-server # → 404 (broken) ``` ## Task Add two well-known discovery endpoint handlers to `main.go` and add a `HEAD` handler to `server.go` that returns the required `MCP-Protocol-Version` header. ## Implementation ### 1. `main.go` — add well-known routes Register before the MCP handler: ```go mux.HandleFunc("/.well-known/oauth-protected-resource", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(`{"resource":"https://git-mcp.d-ma.be","bearer_methods_supported":[],"authorization_servers":[]}`)) }) mux.HandleFunc("/.well-known/oauth-authorization-server", func(w http.ResponseWriter, r *http.Request) { http.NotFound(w, r) }) ``` The empty `authorization_servers` array in the protected-resource response is the signal to clients that this resource requires no OAuth. The authorization-server endpoint returning 404 is consistent with that — there is no AS. ### 2. `internal/mcp/server.go` — add HEAD handler claude.ai also sends `HEAD /mcp` to discover the protocol version before `initialize`. Currently returns 405. Add to `ServeHTTP`: ```go case http.MethodHead: w.Header().Set("MCP-Protocol-Version", ProtocolVersion) w.WriteHeader(http.StatusOK) ``` ### 3. Verify after deploy ```bash # Should return 200 with JSON body curl -si https://git-mcp.d-ma.be/.well-known/oauth-protected-resource # Should return 200 with MCP-Protocol-Version header curl -I https://git-mcp.d-ma.be/mcp # Full initialize handshake should still work curl -s -X POST https://git-mcp.d-ma.be/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","method":"initialize","id":1,"params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"claude.ai","version":"1.0"}}}' | jq . ``` ## Acceptance criteria - `GET /.well-known/oauth-protected-resource` returns 200 with valid JSON containing `authorization_servers: []` - `HEAD /mcp` returns 200 with `MCP-Protocol-Version: 2025-06-18` header - `POST /mcp` initialize handshake continues to work as before - claude.ai custom connector reconnects successfully after deploy ## Branch `fix/oauth-discovery-endpoints` from `main` ## Notes - Do **not** add oauth2-proxy back. It breaks server-to-server MCP calls. - The `resource` field in the protected-resource response must match the public URL exactly. - No new dependencies needed — stdlib only. --- _Created via git-mcp on behalf of @mathiasbq_
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mathias/gitea-mcp#2