Summary
OAuth metadata discovery fails for MCP servers that use path components in their URLs or have separate domains for MCP and OAuth services. This affects servers like Smithery that use:
- MCP Server:
https://server.smithery.ai/googledrive
- Auth Server:
https://auth.smithery.ai/googledrive
Problem
1. Base URL Path Stripping
In client/transport/streamable_http.go (lines 163-168), the base URL construction strips the path:
if smc.oauthHandler != nil {
baseURL := fmt.Sprintf("%s://%s", parsedURL.Scheme, parsedURL.Host)
smc.oauthHandler.SetBaseURL(baseURL)
}
For https://server.smithery.ai/googledrive, this sets baseURL to https://server.smithery.ai, causing:
- PRM discovery at
https://server.smithery.ai/.well-known/oauth-protected-resource (wrong)
- Should be:
https://server.smithery.ai/.well-known/oauth-protected-resource/googledrive (RFC 9728)
2. RFC 8414 Path Insertion Missing
The current code only tries one metadata URL format:
authServerURL + "/.well-known/oauth-authorization-server"
RFC 8414 Section 3.1 specifies that when the issuer URL contains a path, the well-known path should be inserted between host and path:
- Input:
https://auth.smithery.ai/googledrive
- Current:
https://auth.smithery.ai/googledrive/.well-known/oauth-authorization-server ❌
- RFC 8414:
https://auth.smithery.ai/.well-known/oauth-authorization-server/googledrive ✅
3. No WWW-Authenticate Header Parsing
RFC 9728 Section 3 specifies that servers should advertise their Protected Resource Metadata URL via the WWW-Authenticate header:
WWW-Authenticate: Bearer error="invalid_request", resource_metadata="https://server.smithery.ai/.well-known/oauth-protected-resource/googledrive"
The current implementation doesn't parse this header to extract the resource_metadata URL.
Impact
- DCR fails with 404 because the registration endpoint URL is constructed incorrectly
- Authorization URL has empty client_id because DCR failed
- Affects all servers using path-based URLs or separate auth domains (Smithery, potentially others)
Suggested Fix
1. RFC 8414 Compliant URL Construction
// BuildRFC8414MetadataURLs constructs OAuth Authorization Server Metadata URLs per RFC 8414.
func BuildRFC8414MetadataURLs(authServerURL string) []string {
u, err := url.Parse(authServerURL)
if err != nil {
return []string{authServerURL + "/.well-known/oauth-authorization-server"}
}
path := strings.TrimSuffix(u.Path, "/")
baseURL := fmt.Sprintf("%s://%s", u.Scheme, u.Host)
var urls []string
if path == "" || path == "/" {
urls = append(urls, baseURL+"/.well-known/oauth-authorization-server")
} else {
// RFC 8414: insert .well-known between host and path
rfc8414URL := baseURL + "/.well-known/oauth-authorization-server" + path
urls = append(urls, rfc8414URL)
// Legacy fallback
legacyURL := strings.TrimSuffix(authServerURL, "/") + "/.well-known/oauth-authorization-server"
urls = append(urls, legacyURL)
// Base URL fallback (for servers like Cloudflare)
urls = append(urls, baseURL+"/.well-known/oauth-authorization-server")
}
return urls
}
2. WWW-Authenticate Header Parsing
// ExtractResourceMetadataURL parses WWW-Authenticate header for resource_metadata URL
func ExtractResourceMetadataURL(wwwAuthHeader string) string {
if !strings.Contains(wwwAuthHeader, "resource_metadata") {
return ""
}
parts := strings.Split(wwwAuthHeader, `resource_metadata="`)
if len(parts) < 2 {
return ""
}
endIdx := strings.Index(parts[1], `"`)
if endIdx == -1 {
return ""
}
return parts[1][:endIdx]
}
3. Auth Server Discovery from PRM
When the MCP server and OAuth server are on different domains, discover the auth server from Protected Resource Metadata before fetching OAuth metadata.
Related Issue
We encountered this while implementing OAuth support in MCPProxy:
Our workaround implements the discovery logic in mcpproxy, but this would benefit all mcp-go users.
Test Case
Smithery's googledrive server can be used to verify the fix:
# Protected Resource Metadata (RFC 9728)
curl -s "https://server.smithery.ai/.well-known/oauth-protected-resource/googledrive" | jq .
# Returns: {"authorization_servers": ["https://auth.smithery.ai/googledrive"], ...}
# OAuth Authorization Server Metadata (RFC 8414)
curl -s "https://auth.smithery.ai/.well-known/oauth-authorization-server/googledrive" | jq .
# Returns: {"issuer": "...", "registration_endpoint": "...", ...}
# DCR endpoint
curl -s -X POST "https://auth.smithery.ai/googledrive/register" \
-H "Content-Type: application/json" \
-d '{"redirect_uris": ["http://127.0.0.1:8080/callback"]}'
# Returns: {"client_id": "...", "client_secret": "..."}
References
Summary
OAuth metadata discovery fails for MCP servers that use path components in their URLs or have separate domains for MCP and OAuth services. This affects servers like Smithery that use:
https://server.smithery.ai/googledrivehttps://auth.smithery.ai/googledriveProblem
1. Base URL Path Stripping
In
client/transport/streamable_http.go(lines 163-168), the base URL construction strips the path:For
https://server.smithery.ai/googledrive, this sets baseURL tohttps://server.smithery.ai, causing:https://server.smithery.ai/.well-known/oauth-protected-resource(wrong)https://server.smithery.ai/.well-known/oauth-protected-resource/googledrive(RFC 9728)2. RFC 8414 Path Insertion Missing
The current code only tries one metadata URL format:
RFC 8414 Section 3.1 specifies that when the issuer URL contains a path, the well-known path should be inserted between host and path:
https://auth.smithery.ai/googledrivehttps://auth.smithery.ai/googledrive/.well-known/oauth-authorization-server❌https://auth.smithery.ai/.well-known/oauth-authorization-server/googledrive✅3. No WWW-Authenticate Header Parsing
RFC 9728 Section 3 specifies that servers should advertise their Protected Resource Metadata URL via the
WWW-Authenticateheader:The current implementation doesn't parse this header to extract the
resource_metadataURL.Impact
Suggested Fix
1. RFC 8414 Compliant URL Construction
2. WWW-Authenticate Header Parsing
3. Auth Server Discovery from PRM
When the MCP server and OAuth server are on different domains, discover the auth server from Protected Resource Metadata before fetching OAuth metadata.
Related Issue
We encountered this while implementing OAuth support in MCPProxy:
Our workaround implements the discovery logic in mcpproxy, but this would benefit all mcp-go users.
Test Case
Smithery's googledrive server can be used to verify the fix:
References