Skip to content

The resource URL path is ignored when building the protected resource metadata URL #1052

Description

@yurikunash

Initial Checks

Description

The current MCP Server implementation appears to use a fixed URL pattern [domain]/.well-known/oauth-protected-resource for the protected resource URL. After reviewing RFC9728, I believe this doesn't fully align with the specification's requirements.

Expected behavior:
If the resource URL is https://resource.example.com/resource1, the protected resource metadata URL to be https://resource.example.com/.well-known/oauth-protected-resource/resource1

Current behavior:
If the resource URL is https://resource.example.com/resource1, the protected resource metadata URL to be https://resource.example.com/.well-known/oauth-protected-resource

Link to the source code:

"/.well-known/oauth-protected-resource",

return [
        Route(
            "/.well-known/oauth-protected-resource",
            endpoint=cors_middleware(handler.handle, ["GET", "OPTIONS"]),
            methods=["GET", "OPTIONS"],
        )
    ]

RFC 9728

Protected resources supporting metadata MUST make a JSON document containing metadata as specified in Section 2 available at a URL formed by inserting a well-known URI string into the protected resource's resource identifier between the host component and the path and/or query components, if any.
...
The consumer of the metadata would make the following request when the resource identifier is https://resource.example.com/resource1 and the well-known URI path suffix is oauth-protected-resource to obtain the metadata, since the resource identifier contains a path component:

GET /.well-known/oauth-protected-resource/resource1 HTTP/1.1
Host: resource.example.com

As the RFC correctly states:

Using path components enables supporting multiple resources per host. This is required in some multi-tenant hosting configurations.

which is difficult to achieve with the current implementation.

Example Code

Python & MCP Python SDK

Latest version

Activity

  1. pcarleton commented on Jul 4, 2025

    @pcarleton
    Member

    Hi @yurikunash thanks for opening this, I agree with you that we don't implement the more involved path discovery flow currently, and think we should add it like you've described.

    One somewhat finicky nuance is the definition of "resource identifier" that I've had explained to me:

    inserting a well-known URI string into the protected resource's resource identifier between the host component and the path and/or query components

    The server can define the resource identifier to be different than the initial request path. For example, the request might be to https://mcp.example.com/v1/mcp, but the resource identifier could be https://mcp.example.com/v1 or https://mcp.example.com/ if that's how the server organizes things. The resource identifier is meant to be resource being protected, which might be broader than just the MCP endpoint.

    Since we're doing this all dynamically, we can only rely on what the server gives us to determine the resource identifier, and then fallback to defaults. The server can provide context in the WWW-Authenticate header. If it's not there though, we're stuck guessing. The way it is currently is implicitly always assuming it's the base URL, but we should instead check the request URL first, and then check the base URL. (We recently added this behavior for the AS metadata as well, so we can likely re-use some of that.)

  2. yurikunash commented on Jul 16, 2025

    @yurikunash
    ContributorAuthor

    Hi @pcarleton, thank you for your comments. I want to share my simple thoughts on that.
    The FastMCP class exposes a setting settings.auth.resource_server_url. So, why should we guess about the resource identifier and the path, instead of sticking to the value that the server developer has provided through that setting?
    I hope you can either confirm or help find the gap.

  3. added
    authIssues and PRs related to Authentication / OAuth
    ready for workEnough information for someone to start working on
    P3Nice to haves, rare edge cases
    questionFurther information is requested
    on Oct 3, 2025
  4. maxisbey commented on Jun 5, 2026

    @maxisbey
    Contributor

    Both halves of this are implemented and released:

    Both changes are also on main (v2), and the current v1.x release (v1.27.2) carries them. Using the example from this issue: a server configured with resource_server_url=https://resource.example.com/resource1 now serves metadata at https://resource.example.com/.well-known/oauth-protected-resource/resource1.

    The remaining sharp edge — configurations where resource_server_url omits the transport path, making the metadata resource field mismatch what strict clients resolve — is tracked separately in #1264.

    Closing as resolved.

    AI Disclaimer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Nice to haves, rare edge casesauthIssues and PRs related to Authentication / OAuthquestionFurther information is requestedready for workEnough information for someone to start working on

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions