Skip to content

Add invalid_target to AuthorizationErrorCode (RFC 8707) #2641

@stephaneberle9

Description

@stephaneberle9

Summary

mcp/server/auth/provider.py defines AuthorizationErrorCode as a Literal of seven OAuth 2.0 error codes but is missing "invalid_target", the error code defined by RFC 8707 §2 for resource-indicator mismatches. As a result, the auth-handler framework cannot return the correct error code when a downstream client sends an authorization request whose resource parameter doesn't match the protected resource, and any provider that raises AuthorizeError(error="invalid_target", …) triggers a pydantic ValidationError instead of an OAuth-compliant error response.

Current behaviour

In mcp/server/auth/provider.py:

AuthorizationErrorCode = Literal[
    "invalid_request",
    "unauthorized_client",
    "access_denied",
    "unsupported_response_type",
    "invalid_scope",
    "server_error",
    "temporarily_unavailable",
]

AuthorizationErrorResponse.error is typed against this Literal, so when the authorize handler at mcp/server/auth/handlers/authorize.py:123 tries to build AuthorizationErrorResponse(error="invalid_target", …), pydantic rejects it:

pydantic_core._pydantic_core.ValidationError: 1 validation error for AuthorizationErrorResponse
error
  Input should be 'invalid_request', 'unauthorized_client', 'access_denied', 'unsupported_response_type',
  'invalid_scope', 'server_error' or 'temporarily_unavailable'
  [type=literal_error, input_value='invalid_target', input_type=str]

That ValidationError propagates out of the framework's own except block and ultimately surfaces to the downstream client as a generic server_error instead of invalid_target, masking the real cause.

Where it surfaces today

FastMCP's OAuthProxy already raises AuthorizeError(error="invalid_target", …) for RFC 8707 resource-indicator mismatches, with # type: ignore[arg-type] annotations acknowledging the upstream-SDK gap (see fastmcp/server/auth/oauth_proxy/proxy.py:895-898):

raise AuthorizeError(
    error="invalid_target",  # type: ignore[arg-type]  # ty:ignore[invalid-argument-type]
    error_description="Resource does not match this server",
)

In practice this means any MCP proxy fronting multiple resources cannot return spec-compliant errors when a client's resource parameter doesn't match, and operators see misleading server_error responses in logs and clients see a generic OAuth error.

Proposed fix

Add "invalid_target" to AuthorizationErrorCode:

AuthorizationErrorCode = Literal[
    "invalid_request",
    "unauthorized_client",
    "access_denied",
    "unsupported_response_type",
    "invalid_scope",
    "server_error",
    "temporarily_unavailable",
    "invalid_target",  # RFC 8707
]

No other changes required — AuthorizationErrorResponse and AuthorizeError already accept the Literal type by reference.

Environment

  • mcp 1.27.1
  • fastmcp 3.3.1
  • Python 3.13

Happy to submit a PR if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions