11from dataclasses import dataclass
2+ from typing import Callable
23
3- from pydantic import AnyUrl
4+ from pydantic import AnyHttpUrl
45from starlette .routing import Route , Router
56
67from mcp .server .auth .handlers .authorize import AuthorizationHandler
@@ -24,7 +25,7 @@ class RevocationOptions:
2425 enabled : bool = False
2526
2627
27- def validate_issuer_url (url : AnyUrl ):
28+ def validate_issuer_url (url : AnyHttpUrl ):
2829 """
2930 Validate that the issuer URL meets OAuth 2.0 requirements.
3031
@@ -58,8 +59,8 @@ def validate_issuer_url(url: AnyUrl):
5859
5960def create_auth_router (
6061 provider : OAuthServerProvider ,
61- issuer_url : AnyUrl ,
62- service_documentation_url : AnyUrl | None = None ,
62+ issuer_url : AnyHttpUrl ,
63+ service_documentation_url : AnyHttpUrl | None = None ,
6364 client_registration_options : ClientRegistrationOptions | None = None ,
6465 revocation_options : RevocationOptions | None = None ,
6566) -> Router :
@@ -134,34 +135,61 @@ def create_auth_router(
134135 return auth_router
135136
136137
138+ def modify_url_path (url : AnyHttpUrl , path_mapper : Callable [[str ], str ]) -> AnyHttpUrl :
139+ return AnyHttpUrl .build (
140+ scheme = url .scheme ,
141+ username = url .username ,
142+ password = url .password ,
143+ host = url .host ,
144+ port = url .port ,
145+ path = path_mapper (url .path or "" ),
146+ query = url .query ,
147+ fragment = url .fragment ,
148+ )
149+
150+
137151def build_metadata (
138- issuer_url : AnyUrl ,
139- service_documentation_url : AnyUrl | None ,
152+ issuer_url : AnyHttpUrl ,
153+ service_documentation_url : AnyHttpUrl | None ,
140154 client_registration_options : ClientRegistrationOptions ,
141155 revocation_options : RevocationOptions ,
142156) -> OAuthMetadata :
143- issuer_url_str = str (issuer_url ).rstrip ("/" )
157+ authorization_url = modify_url_path (
158+ issuer_url , lambda path : path .rstrip ("/" ) + AUTHORIZATION_PATH .lstrip ("/" )
159+ )
160+ token_url = modify_url_path (
161+ issuer_url , lambda path : path .rstrip ("/" ) + TOKEN_PATH .lstrip ("/" )
162+ )
144163 # Create metadata
145164 metadata = OAuthMetadata (
146- issuer = issuer_url_str ,
147- service_documentation = str (service_documentation_url ).rstrip ("/" )
148- if service_documentation_url
149- else None ,
150- authorization_endpoint = f"{ issuer_url_str } { AUTHORIZATION_PATH } " ,
165+ issuer = issuer_url ,
166+ authorization_endpoint = authorization_url ,
167+ token_endpoint = token_url ,
168+ scopes_supported = None ,
151169 response_types_supported = ["code" ],
152- code_challenge_methods_supported = ["S256" ],
153- token_endpoint = f"{ issuer_url_str } { TOKEN_PATH } " ,
154- token_endpoint_auth_methods_supported = ["client_secret_post" ],
170+ response_modes_supported = None ,
155171 grant_types_supported = ["authorization_code" , "refresh_token" ],
172+ token_endpoint_auth_methods_supported = ["client_secret_post" ],
173+ token_endpoint_auth_signing_alg_values_supported = None ,
174+ service_documentation = service_documentation_url ,
175+ ui_locales_supported = None ,
176+ op_policy_uri = None ,
177+ op_tos_uri = None ,
178+ introspection_endpoint = None ,
179+ code_challenge_methods_supported = ["S256" ],
156180 )
157181
158182 # Add registration endpoint if supported
159183 if client_registration_options .enabled :
160- metadata .registration_endpoint = f"{ issuer_url_str } { REGISTRATION_PATH } "
184+ metadata .registration_endpoint = modify_url_path (
185+ issuer_url , lambda path : path .rstrip ("/" ) + REGISTRATION_PATH .lstrip ("/" )
186+ )
161187
162188 # Add revocation endpoint if supported
163189 if revocation_options .enabled :
164- metadata .revocation_endpoint = f"{ issuer_url_str } { REVOCATION_PATH } "
190+ metadata .revocation_endpoint = modify_url_path (
191+ issuer_url , lambda path : path .rstrip ("/" ) + REVOCATION_PATH .lstrip ("/" )
192+ )
165193 metadata .revocation_endpoint_auth_methods_supported = ["client_secret_post" ]
166194
167195 return metadata
0 commit comments