@@ -99,24 +99,36 @@ def get_client_metadata_scopes(
9999 www_authenticate_scope : str | None ,
100100 protected_resource_metadata : ProtectedResourceMetadata | None ,
101101 authorization_server_metadata : OAuthMetadata | None = None ,
102+ client_grant_types : list [str ] | None = None ,
102103) -> str | None :
103- """Select scopes as outlined in the 'Scope Selection Strategy' in the MCP spec."""
104- # Per MCP spec, scope selection priority order:
105- # 1. Use scope from WWW-Authenticate header (if provided)
106- # 2. Use all scopes from PRM scopes_supported (if available)
107- # 3. Omit scope parameter if neither is available
108-
104+ """Select effective scopes and augment for refresh token support."""
105+ selected_scope : str | None = None
106+
107+ # MCP spec scope selection priority:
108+ # 1. WWW-Authenticate header scope
109+ # 2. PRM scopes_supported
110+ # 3. AS scopes_supported (SDK fallback)
111+ # 4. Omit scope parameter
109112 if www_authenticate_scope is not None :
110- # Priority 1: WWW-Authenticate header scope
111- return www_authenticate_scope
113+ selected_scope = www_authenticate_scope
112114 elif protected_resource_metadata is not None and protected_resource_metadata .scopes_supported is not None :
113- # Priority 2: PRM scopes_supported
114- return " " .join (protected_resource_metadata .scopes_supported )
115+ selected_scope = " " .join (protected_resource_metadata .scopes_supported )
115116 elif authorization_server_metadata is not None and authorization_server_metadata .scopes_supported is not None :
116- return " " .join (authorization_server_metadata .scopes_supported ) # pragma: no cover
117- else :
118- # Priority 3: Omit scope parameter
119- return None
117+ selected_scope = " " .join (authorization_server_metadata .scopes_supported ) # pragma: no cover
118+
119+ # SEP-2207: append offline_access when the AS supports it and the client can use refresh tokens
120+ if (
121+ selected_scope is not None
122+ and authorization_server_metadata is not None
123+ and authorization_server_metadata .scopes_supported is not None
124+ and "offline_access" in authorization_server_metadata .scopes_supported
125+ and client_grant_types is not None
126+ and "refresh_token" in client_grant_types
127+ and "offline_access" not in selected_scope .split ()
128+ ):
129+ selected_scope = f"{ selected_scope } offline_access"
130+
131+ return selected_scope
120132
121133
122134def build_oauth_authorization_server_metadata_discovery_urls (auth_server_url : str | None , server_url : str ) -> list [str ]:
0 commit comments