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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions