What happened?
When connecting to an MCP server supporting the oauth-protected-resource protocol and has a path component in its URL, gemini-cli fails to authenticate with an error like:
✕ Failed to authenticate with MCP server: Protected resource https://example.com does not match expected https://example.com/path/to/mcp
Root Cause
Per RFC 9728 §3.1:
The protected resource metadata for a resource is obtained by sending an HTTP GET request to a path formed by inserting a well-known URI string into the resource URL between the host and path components [...] If the resource identifier value contains a path component, any terminating "/" character is removed and the remaining path is appended to the well-known URI.
E.g., for a resource at https://example.com/api/resource, the correct well-known URL is:
https://example.com/.well-known/oauth-protected-resource/api/resource
Not the root:
https://example.com/.well-known/oauth-protected-resource
This is to support multi-tenant resource configurations.
Current Behavior
gemini-cli currently tries root-based discovery first, then falls back to path-based discovery:
- Client requests https://example.com/.well-known/oauth-protected-resource (root)
- RFC 9728-compliant server responds with {"resource": "https://example.com", ...}
- Client tries to validate that resource matches the original URL (https://example.com/api/resource)
- Validation fails: "https://example.com does not match https://example.com/api/resource"
I believe this is not a bug in the server. Per RFC 9728, the server at the root well-known endpoint correctly returns the root resource.
I believe the bug is that gemini-cli should not be requesting the root endpoint for a resource with a path, at least as the first option.
What did you expect to happen?
Expected Behavior
gemini-cli should try path-based discovery first (RFC 9728 compliant), then optionally fall back to root-based discovery for backwards compatibility with non-compliant servers.
Reference Implementation
The official MCP TypeScript SDK uses path-based discovery by default with no root fallback:
// https://github.com/modelcontextprotocol/typescript-sdk/blob/6b4d99f10b975d65392bb777cc8cb1151c20c972/packages/server/src/server/auth/router.ts#L242
export function getOAuthProtectedResourceMetadataUrl(serverUrl: URL): string {
const u = new URL(serverUrl.href);
const rsPath = u.pathname && u.pathname !== '/' ? u.pathname : '';
return new URL(/.well-known/oauth-protected-resource${rsPath}, u).href;
}
Affected Code
packages/core/src/mcp/oauth-utils.ts: buildWellKnownUrls() and discoverOAuthConfig()
Client information
Client Information
Run gemini to enter the interactive CLI, then run the /about command.
> /about
CLI Version 0.23.0-preview.3 │
│ Git Commit 703d2e0dc │
│ Model auto-gemini-3 │
│ Sandbox no sandbox │
│ OS darwin │
│ Auth Method OAuth │
│ User Email vrv@d8e.ai```
</details>
### Login information
_No response_
### Anything else we need to know?
I've implemented a fix which I will link, which switches the order in which these paths are tried, starting with path-based first and then falling back to the root. I don't believe the latter is necessary according to the spec, but leaving it in unless you tell me not to, since I assume some servers might be doing the wrong thing and I don't want to break them :)
Thanks!
What happened?
When connecting to an MCP server supporting the oauth-protected-resource protocol and has a path component in its URL, gemini-cli fails to authenticate with an error like:
✕ Failed to authenticate with MCP server: Protected resource https://example.com does not match expected https://example.com/path/to/mcp
Root Cause
Per RFC 9728 §3.1:
The protected resource metadata for a resource is obtained by sending an HTTP GET request to a path formed by inserting a well-known URI string into the resource URL between the host and path components [...] If the resource identifier value contains a path component, any terminating "/" character is removed and the remaining path is appended to the well-known URI.
E.g., for a resource at https://example.com/api/resource, the correct well-known URL is:
https://example.com/.well-known/oauth-protected-resource/api/resource
Not the root:
https://example.com/.well-known/oauth-protected-resource
This is to support multi-tenant resource configurations.
Current Behavior
gemini-cli currently tries root-based discovery first, then falls back to path-based discovery:
I believe this is not a bug in the server. Per RFC 9728, the server at the root well-known endpoint correctly returns the root resource.
I believe the bug is that gemini-cli should not be requesting the root endpoint for a resource with a path, at least as the first option.
What did you expect to happen?
Expected Behavior
gemini-cli should try path-based discovery first (RFC 9728 compliant), then optionally fall back to root-based discovery for backwards compatibility with non-compliant servers.
Reference Implementation
The official MCP TypeScript SDK uses path-based discovery by default with no root fallback:
// https://github.com/modelcontextprotocol/typescript-sdk/blob/6b4d99f10b975d65392bb777cc8cb1151c20c972/packages/server/src/server/auth/router.ts#L242
export function getOAuthProtectedResourceMetadataUrl(serverUrl: URL): string {
const u = new URL(serverUrl.href);
const rsPath = u.pathname && u.pathname !== '/' ? u.pathname : '';
return new URL(
/.well-known/oauth-protected-resource${rsPath}, u).href;}
Affected Code
packages/core/src/mcp/oauth-utils.ts: buildWellKnownUrls() and discoverOAuthConfig()
Client information
Client Information
Run
geminito enter the interactive CLI, then run the/aboutcommand.