
    i?                    b   d dl mZ d dlZd dlZd dlmZ d dlmZ d dlm	Z	 d dl
Z
d dlZd dlmZ d dlmZ d dlmZ d d	lmZmZ d d
lmZ d dlmZmZmZ d dlmZ d dlmZ d dlm Z  d dl!m"Z"m#Z# d dl$m%Z% d dl&m'Z' dgZ( e'e)      Z* G d de+      Z,	 d	 	 	 	 	 ddZ- G d de      Z. G d de      Z/y)    )annotationsN)AsyncGenerator)aclosing)Any)PydanticAdapter)AsyncKeyValue)MemoryStore)OAuthClientProviderTokenStorage)McpHttpClientFactory)OAuthClientInformationFullOAuthClientMetadata
OAuthToken)
AnyHttpUrl)override)Server)OAuthCallbackResultcreate_oauth_callback_server)find_available_port)
get_loggerOAuthc                      e Zd ZdZy)ClientNotFoundErrorzARaised when OAuth client credentials are not found on the server.N)__name__
__module____qualname____doc__     i/Users/bowang/.openclaw/workspace/ChatDev/.venv/lib/python3.12/site-packages/fastmcp/client/auth/oauth.pyr   r   %   s    Kr   r   c                  K   t        j                  di |xs i 4 d{   }	 |j                  | d       d{   }|j                  dv r	 ddd      d{    yd|j                  v r	 ddd      d{    y	 ddd      d{    y7 r7 X7 ;7 7 # t         j
                  $ r Y ddd      d{  7   yw xY w# 1 d{  7  sw Y   yxY ww)	z
    Check if the MCP endpoint requires authentication by making a test request.

    Returns:
        True if auth appears to be required, False otherwise
    Ng      @)timeout)i  i  TzWWW-AuthenticateFr   )httpxAsyncClientgetstatus_codeheadersRequestError)mcp_urlhttpx_kwargsclientresponses       r    check_if_auth_requiredr-   )   s        8L$6B88F	#ZZZ==H ##z1 988 "X%5%55 988  988 > 988  !! 	% 988 	! 988s   CBCCBBBCBC!B0C;B<CCBCBCCCC0C1C<B?=CCCCCCCc                      e Zd ZU ded<   ded<   ded<   ded<   dd	Zdd
ZddZddZedd       Z	edd       Z
edd       Zedd       Zy)TokenStorageAdapterstr_server_urlr   _key_value_storezPydanticAdapter[OAuthToken]_storage_oauth_tokenz+PydanticAdapter[OAuthClientInformationFull]_storage_client_infoc                    || _         || _        t        t           d|t        d      | _        t        t
           d|t
        d      | _        y )Nzmcp-oauth-tokenT)default_collection	key_valuepydantic_modelraise_on_validation_errorzmcp-oauth-client-info)r1   r2   r   r   r3   r   r4   )selfasync_key_value
server_urls      r    __init__zTokenStorageAdapter.__init__M   sQ    % /$3J$?0%%&*	%
! %44N$O6%5&*	%
!r   c                     | j                    dS )Nz/tokensr1   r:   s    r    _get_token_cache_keyz(TokenStorageAdapter._get_token_cache_key]   s    ""#7++r   c                     | j                    dS )Nz/client_infor?   r@   s    r    _get_client_info_cache_keyz.TokenStorageAdapter._get_client_info_cache_key`   s    ""#<00r   c                   K   | j                   j                  | j                                d {    | j                  j                  | j	                                d {    y 7 77 wN)key)r3   deleterA   r4   rC   r@   s    r    clearzTokenStorageAdapter.clearc   s\     ''..43L3L3N.OOO''..43R3R3T.UUU 	PUs!   .A,A(1A,"A*#A,*A,c                r   K   | j                   j                  | j                                d {   S 7 wrE   )r3   r%   rA   r@   s    r    
get_tokenszTokenStorageAdapter.get_tokensg   s/     ..22t7P7P7R2SSSS   .757c                x   K   | j                   j                  | j                         |d       d {    y 7 w)Ni3rF   valuettl)r3   putrA   )r:   tokenss     r    
set_tokenszTokenStorageAdapter.set_tokensk   s=     
 ''++))+" , 
 	
 	
s   0:8:c                r   K   | j                   j                  | j                                d {   S 7 wrE   )r4   r%   rC   r@   s    r    get_client_infoz#TokenStorageAdapter.get_client_infov   s:     ..22//1 3 
 
 	
 
rK   c                   K   d }|j                   r*|j                   t        t        j                               z
  }| j                  j	                  | j                         ||       d {    y 7 w)NrM   )client_secret_expires_atinttimer4   rP   rC   )r:   client_inforO   s      r    set_client_infoz#TokenStorageAdapter.set_client_info|   sd     //66TYY[9IIC''++//1 , 
 	
 	
s   A(A2*A0+A2N)r;   r   r<   r0   )returnr0   r[   None)r[   zOAuthToken | None)rQ   r   r[   r]   )r[   z!OAuthClientInformationFull | None)rY   r   r[   r]   )r   r   r   __annotations__r=   rA   rC   rH   r   rJ   rR   rT   rZ   r   r   r    r/   r/   G   s    ##55EE
 ,1V T T 
 
 
 

 

 

r   r/   c                       e Zd ZU dZded<   	 	 	 	 	 	 	 	 	 	 d
	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZd fdZd fdZddZddZ		 	 	 	 d fd	Z
 xZS )r   z
    OAuth client provider for MCP servers with browser-based authentication.

    This class provides OAuth authentication for FastMCP clients by opening
    a browser for user authorization and running a local callback server.
    bool_boundc                    || _         || _        || _        || _        || _        || _        |	| _        |
| _        d| _        |xs t        j                  | _        d| _        || j                  |       yy)a*  
        Initialize OAuth client provider for an MCP server.

        Args:
            mcp_url: Full URL to the MCP endpoint (e.g. "http://host/mcp/sse/").
                Optional when OAuth is passed to Client(auth=...), which provides
                the URL automatically from the transport.
            scopes: OAuth scopes to request. Can be a
            space-separated string or a list of strings.
            client_name: Name for this client during registration
            token_storage: An AsyncKeyValue-compatible token store, tokens are stored in memory if not provided
            additional_client_metadata: Extra fields for OAuthClientMetadata
            callback_port: Fixed port for OAuth callback (default: random available port)
            client_metadata_url: A CIMD (Client ID Metadata Document) URL. When
                provided, this URL is used as the client_id instead of performing
                Dynamic Client Registration. Must be an HTTPS URL with a non-root
                path (e.g. "https://myapp.example.com/oauth/client.json").
            client_id: Pre-registered OAuth client ID. When provided, skips dynamic
                client registration and uses these static credentials instead.
            client_secret: OAuth client secret (optional, used with client_id)
        NF)_scopes_client_name_token_storage_additional_client_metadata_callback_port_client_metadata_url
_client_id_client_secret_static_client_infor#   r$   httpx_client_factoryra   _bind)r:   r)   scopesclient_nametoken_storageadditional_client_metadatacallback_portrl   client_metadata_url	client_idclient_secrets              r    r=   zOAuth.__init__   s|    L '++E(+$7!#+#' $8$ME<M<M!JJw r   c           	        | j                   ry|j                  d      }| j                  xs
 t               | _        d| j                   d}t        | j                  t              rdj                  | j                        }n$| j                  t        | j                        }nd}t        d| j                  t        |      gddgd	g|d
| j                  xs i }| j                  rP|j                  d      }d|vr| j                   rdnd|d<   t#        d| j                  | j                   d|| _        | j&                  xs
 t)               }t        |t(              rddlm}  |dd       t/        ||      | _        || _        t4        | m  ||| j0                  | j8                  | j:                  | j<                         d| _         y)zBind this OAuth provider to a specific MCP server URL.

        Called automatically when mcp_url is provided to __init__, or by the
        transport when OAuth is used without an explicit URL.
        N/zhttp://localhost:z	/callback  authorization_coderefresh_tokencode)ro   redirect_urisgrant_typesresponse_typesscopeT)exclude_nonetoken_endpoint_auth_methodclient_secret_postnone)rt   ru   r   )warnzUsing in-memory token storage -- tokens will be lost when the client restarts. For persistent storage across multiple MCP servers, provide an encrypted AsyncKeyValue backend. See https://gofastmcp.com/clients/auth/oauth#token-storage for details.   )message
stacklevel)r;   r<   )r<   client_metadatastorageredirect_handlercallback_handlerrs   r   )ra   rstriprg   r   redirect_port
isinstancerc   listjoinr0   r   rd   r   rf   ri   
model_dumprj   r   rk   re   r	   warningsr   r/   token_storage_adapterr)   superr=   r   r   rh   )	r:   r)   redirect_uri
scopes_strr   metadatarp   r   	__class__s	           r    rm   zOAuth._bind   s    ;;..%!00I4G4I*4+=+=*>iH dllD)$,,/J\\%T\\*JJ- 
))%l34-?"8
 //52
 ?? '11t1DH ,8;,0,?,?(V 56 (B (//"11( (D$ ++<{}m[1%\ 	 ;N)g;
" +..!22!22 $ 9 9 	 	
 r   c                  K   t         |           d{    | j                  H| j                  | j                  _        | j
                  j                  | j                         d{    | j                  j                  rQ| j                  j                  j                  r0| j                  j                  | j                  j                         yyy7 7 nw)zBLoad stored tokens and client info, properly setting token expiry.N)
r   _initializerk   contextrY   r   rZ   current_tokens
expires_inupdate_token_expiry)r:   r   s    r    r   zOAuth._initialize  s     g!#####/'+'?'?DLL$,,<<T=U=UVVV<<&&4<<+F+F+Q+QLL,,T\\-H-HI ,R& 	$ Ws#   CCAC)C*A-CCc                  K   | j                         4 d{   }|j                  |d       d{   }|j                  dk(  rt        d      |j                  dvrt	        d|j                         ddd      d{    t
        j                  d|        t        j                  |       y7 7 7 6# 1 d{  7  sw Y   FxY ww)	zIOpen browser for authorization, with pre-flight check for invalid client.NF)follow_redirectsi  z8OAuth client not found - cached credentials may be stale)   i.  i/  i3  i4  z#Unexpected authorization response: zOAuth authorization URL: )	rl   r%   r&   r   RuntimeErrorloggerinfo
webbrowseropen)r:   authorization_urlr+   r,   s       r    r   zOAuth.redirect_handler"  s      ,,..&#ZZ(9EZRRH ##s*)N 
 ##+DD"9(:N:N9OP  /. 	/0A/BCD)*! /R /...sU   CB5CB;B7AB;7CB93C7B;9C;CCC	Cc                  K   t               }t        j                         }t        | j                  | j
                  ||      }t        j                         4 d{   }|j                  |j                         t        j                  d| j                          d}	 t        j                  |      5  |j                          d{    |j                  r|j                  |j                  |j                  fcddd       d|_        t        j"                  d       d{    |j$                  j'                          cddd      d{    S 7 7 7 47 # 1 sw Y   nxY wn!# t(        $ r}t)        d| d      |d}~ww xY w	 d|_        t        j"                  d       d{  7   |j$                  j'                          nD# d|_        t        j"                  d       d{  7   |j$                  j'                          w xY wddd      d{  7   t+        d	      # 1 d{  7  sw Y   t+        d	      xY ww)
z4Handle OAuth callback and return (auth_code, state).)portr<   result_containerresult_readyNu7   🎧 OAuth callback server started on http://localhost:g     r@Tg?zOAuth callback timed out after z secondsz+OAuth callback handler could not be started)r   anyioEventr   r   r)   create_task_group
start_soonserver   r   
fail_afterwaiterrorr|   stateshould_exitsleepcancel_scopecancelTimeoutErrorr   )r:   resultr   servertgTIMEOUTes          r    r   zOAuth.callback_handler7  s     %&{{} 6##||#%	
 **,,MM&,,'KKI$J\J\I]^ G)%%g.&++---||$ll*!;;4	 /. &*"kk#&&&&&() -,, . '' - /..
   "5gYhG / &*"kk#&&&&&( &*"kk#&&&&&() -,,, HII- -,,, HIIs   AIEI A H.!E,6E
E3E>	E,H.&E'H.IEIEH.IE(	$E,+G,	F
5FF

GH.-F0. H. H.G1
/ HH.IH I.I
4H75I
<Ic                 K   | j                   st        d      	 t        t        |   |            4 d{   }d}	 	 |j                  |       d{   }|} 7 '7 # t        $ r Y nw xY wddd      d{  7   y# 1 d{  7  sw Y   yxY w# t        $ r | j                  t        d      dt        j                  d       d| _        | j                  j                          d{  7   t        t        |   |            4 d{  7  }d}	 	 |j                  |       d{  7  }|}n# t        $ r Y nw xY w1ddd      d{  7   Y y# 1 d{  7  sw Y   Y yxY ww xY ww)zHTTPX auth flow with automatic retry on stale cached credentials.

        If the OAuth flow fails due to invalid/stale client credentials,
        clears the cache and retries once with fresh registration.
        zOAuth provider has no server URL. Either pass mcp_url to OAuth() or use it with Client(auth=...) which provides the URL automatically.NzOAuth server rejected the static client credentials. Verify that the client_id (and client_secret, if provided) are correct and that the client is registered with the server.z@OAuth client not found on server, clearing cache and retrying...F)ra   r   r   r   async_auth_flowasendStopAsyncIterationr   rk   r   debug_initializedr   rH   )r:   requestgenr,   yielded_requestr   s        r    r   zOAuth.async_auth_flow^  sp     {{X %	 7 @AAS03		(0C*C)8#8	  B
 +D-  BAAAA # 	 ''3)U 	 LLR !&D,,22444   7 @AAS03		(0C*C*C)8#8- 	  BAAAA%	s(  E=B A B BA$A"	A$B B "A$$	A0-B/A00B3B >B?B E=BBBB E=B AE:4C75!E:DE:E# E 4D75
E ?E# 	E	E#EE#E:EE:!E=#E6	)E,*E6	1E:4E=6E::E=)
NNzFastMCP ClientNNNNNNN)r)   
str | Nonern   zstr | list[str] | Nonero   r0   rp   zAsyncKeyValue | Nonerq   dict[str, Any] | Nonerr   z
int | Nonerl   zMcpHttpClientFactory | Noners   r   rt   r   ru   r   )r)   r0   r[   r]   r\   )r   r0   r[   r]   )r[   ztuple[str, str | None])r   zhttpx.Requestr[   z-AsyncGenerator[httpx.Request, httpx.Response])r   r   r   r   r^   r=   rm   r   r   r   r   __classcell__)r   s   @r    r   r      s     L #)-+.2<@$(<@ +/ $$(3 3  '3  	3 
 ,3  %:3  "3  :3  (3  3  "3 jL\	J+*%JN2$2	62 2r   )N)r)   r0   r*   r   r[   r`   )0
__future__r   rX   r   collections.abcr   
contextlibr   typingr   r   r#   key_value.aio.adapters.pydanticr   key_value.aio.protocolsr   key_value.aio.stores.memoryr	   mcp.client.authr
   r   mcp.shared._httpx_utilsr   mcp.shared.authr   r   r   pydanticr   typing_extensionsr   uvicorn.serverr   fastmcp.client.oauth_callbackr   r   fastmcp.utilities.httpr   fastmcp.utilities.loggingr   __all__r   r   	Exceptionr   r-   r/   r   r   r   r    <module>r      s    "   *     ; 1 3 = 8 
   & ! 7 0)	H	L) L
 9= 5	<@
, @
FF Fr   