auth: OAuth client rejects protected resource metadata when the MCP server URL has a query string

Open
#1,519 2 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@beiryu is already working on this.

Since Nov 12, 2026.

  • #1525 by @beiryu — closed without merging

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

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: checkResourceAllowed compares origin and path only.
  • Python: check_resource_allowed does the same, and uses the metadata's resource.
  • Rust: is_resource_identifier_valid notes "Query parameters may carry transport hints rather than resource identity."

Additional context

  • go-sdk v1.8.0; main has the same code (oauthex/resource_meta.go, auth/authorization_code.go).
  • protectedResourceMetadataURLs copies 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

Open in Codespaces

Starts the project's dev container in your browser, under your own GitHub account.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from modelcontextprotocol/go-sdk

All issues in modelcontextprotocol/go-sdk

Similar issues

More Go issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.