auth: OAuth client rejects protected resource metadata when the MCP server URL has a query string
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 74/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- go
- Domain
- api, authentication
Research direction
Start with oauthex/resource_meta.go and auth/authorization_code.go, which the issue identifies as containing the URL handling and strict resource comparison. Trace how query and fragment components reach metadata candidate URLs and the resource parameter, then check the OAuth metadata tests. Done when a server URL with a query or fragment matches metadata naming the same URL without those components, and the resource parameter uses that normalized URL.
Written by the indexing model from the issue text.
Description
Describe the bug
The OAuth client fails to authorize against an MCP server whose URL carries a query string, when the server's Protected Resource Metadata names the resource without that query.
AuthorizationCodeHandler passes req.URL.String(), query included, as the resource URL. oauthex.GetProtectedResourceMetadata then compares it to the metadata's resource with !=, so every candidate is refused and the handler falls back to treating the server root as the authorization server.
RFC 8707 section 2 says a resource indicator "SHOULD NOT include a query component", so a server that takes configuration in its URL (for example https://mcp.example.com/mcp?features=flags) publishes "resource": "https://mcp.example.com/mcp", and its WWW-Authenticate challenge points at metadata for that resource.
This is the same strict comparison as #886 (trailing slash), for the query component.
To Reproduce
func TestQueryInServerURL(t *testing.T) {
var base string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{
"resource": base + "/mcp",
"authorization_servers": []string{"https://as.example"},
})
}))
defer srv.Close()
base = srv.URL
meta := base + "/.well-known/oauth-protected-resource/mcp"
if _, err := oauthex.GetProtectedResourceMetadata(context.Background(), meta, base+"/mcp", srv.Client()); err != nil {
t.Fatal(err) // passes
}
if _, err := oauthex.GetProtectedResourceMetadata(context.Background(), meta, base+"/mcp?features=flags", srv.Client()); err != nil {
t.Fatal(err) // got metadata resource ".../mcp", want ".../mcp?features=flags"
}
}
Expected behavior
The server URL's query (and fragment) is dropped before the resource is compared and before it is sent as the resource parameter.
The other official SDKs accept this case:
- TypeScript:
checkResourceAllowedcompares origin and path only. - Python:
check_resource_alloweddoes the same, and uses the metadata's resource. - Rust:
is_resource_identifier_validnotes "Query parameters may carry transport hints rather than resource identity."
Additional context
- go-sdk v1.8.0;
mainhas the same code (oauthex/resource_meta.go,auth/authorization_code.go). protectedResourceMetadataURLscopies the URL with its query too, so the well-known candidates it builds also carry the query.
- Dominant language
- Go
- Stars
- 5.3k
- Forks
- 614
- Avg merge
- 23h 55m
- Merged PRs (30d)
- 62
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from modelcontextprotocol/go-sdk
-
mcp: typed tool handlers lose wrapped JSON-RPC errorsMay be free again A pull request for this issue was closed without being merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
modelcontextprotocol/go-sdk#1529 · 1 comment ·
Maintainers usually reply within 1 day
-
Expose generic `SendNotification` on `ServerSession` for custom protocol extensionsMay be free again A pull request for this issue was closed without being merged. Openneeds investigation
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
modelcontextprotocol/go-sdk#745 · 13 comments · 1 reaction ·
Maintainers usually reply within 1 day
-
mcp: ClientSession.ReadResource keeps uncacheable and expired results until the session endsMay be free again A pull request for this issue was closed without being merged. Open
Difficulty 3/5 1-2 days Newbie friendliness 74/100
modelcontextprotocol/go-sdk#1526 ·
Maintainers usually reply within 1 day
-
mcp: streamable client keeps the session ID from a rejected server/discover, so the fallback initialize failsPossibly taken @simon0191 claimed this 1 day ago. Open
modelcontextprotocol/go-sdk#1522 · 1 comment · 1 assignee ·
Maintainers usually reply within 1 day
-
`ping` and `logging/setLevel` still sent for protocol 2026-07-28Possibly taken @nahapetyan-serob claimed this 1 day ago. Open
Difficulty 3/5 1-2 days Newbie friendliness 65/100
modelcontextprotocol/go-sdk#1518 · 1 comment · 1 assignee ·
Maintainers usually reply within 1 day
All issues in modelcontextprotocol/go-sdk
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
-
evener-transcript-v2-upgrade tool still scans transcript files through bufio's 4 KiB default bufferOpen
Difficulty 1/5 Under an hour Newbie friendliness 78/100
prime-radiant-inc/evener#6370 ·
Maintainers usually reply within 1 day
-
ManageSieve proxy: a backend's NO/BYE greeting is skipped as a capability line and reported as EOFOpenbug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
Mend: dependency security vulnerability
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
-
Mend: dependency security vulnerability
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
ManageIQ/manageiq-pods#1476 ·
Maintainers usually reply within 1 day