
    kKjG                    r   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*ddZ+ddZ, G d de-      Z.	 d	 	 	 	 	 ddZ/ G d de      Z0 G d de      Z1y)    )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                T    | j                  d      r| j                  d      r| dd S | S )N[]   )
startswithendswithhosts    j/Users/ahmed/devFolder/Ultron/claude-voice/.venv/lib/python3.12/site-packages/fastmcp/client/auth/oauth.py!_normalize_callback_host_for_bindr"   %   s*    sc 2AbzK    c                    d| v rd|  dS | S )N:r   r    r   s    r!   _format_callback_host_for_urlr'   +   s    
d{4&{Kr#   c                      e Zd ZdZy)ClientNotFoundErrorzARaised when OAuth client credentials are not found on the server.N)__name__
__module____qualname____doc__r&   r#   r!   r)   r)   1   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:   5   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ddZe	dd       Z
e	dd       Z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)r>   r?   r   r   r@   r   rA   )selfasync_key_value
server_urls      r!   __init__zTokenStorageAdapter.__init__Y   sQ    % /$3J$?0%%&*	%
! %44N$O6%5&*	%
!r#   c                     | j                    dS )Nz/tokensr>   rG   s    r!   _get_token_cache_keyz(TokenStorageAdapter._get_token_cache_keyi   s    ""#7++r#   c                     | j                    dS )Nz/client_inforL   rM   s    r!   _get_client_info_cache_keyz.TokenStorageAdapter._get_client_info_cache_keyl   s    ""#<00r#   c                     | j                    dS )Nz/token_expiryrL   rM   s    r!   _get_token_expiry_cache_keyz/TokenStorageAdapter._get_token_expiry_cache_keyo   s    ""#=11r#   c                F  K   | j                   j                  | j                                d {    | j                  j                  | j	                                d {    | j
                  j                  | j                         d       d {    y 7 j7 :7 	w)Nkeymcp-oauth-token-expiryrU   
collection)r@   deleterN   rA   rP   r?   rR   rM   s    r!   clearzTokenStorageAdapter.clearr   s     ''..43L3L3N.OOO''..43R3R3T.UUU##**002/ + 
 	
 	
 	PU	
s3   .B!B1B!"B#2B!BB!B!B!c                r   K   | j                   j                  | j                                d {   S 7 wNrT   )r@   r2   rN   rM   s    r!   
get_tokenszTokenStorageAdapter.get_tokensz   s/     ..22t7P7P7R2SSSS   .757c                X  K   | j                   j                  | j                         |d       d {    |j                  bt	        j                         t        |j                        z   }| j                  j                  | j                         d|idd       d {    y y 7 s7 w)Ni3rU   valuettl
expires_atrV   )rU   ra   rX   rb   )r@   putrN   
expires_intimeintr?   rR   )rG   tokensrc   s      r!   
set_tokenszTokenStorageAdapter.set_tokens~   s     
 ''++))+" , 
 	
 	
 (s6+<+<'==J''++446#Z03&	 ,    )	
s"   0B*B&A,B*B( B*(B*c                   K   | j                   j                  | j                         d       d {   }|t        |d         S y 7 w)NrV   rW   rc   )r?   r2   rR   float)rG   raws     r!   get_token_expiryz$TokenStorageAdapter.get_token_expiry   sT     ))--002/ . 
 
 ?\*++
s   /A	AA	c                r   K   | j                   j                  | j                                d {   S 7 wr\   )rA   r2   rP   rM   s    r!   get_client_infoz#TokenStorageAdapter.get_client_info   s:     ..22//1 3 
 
 	
 
r^   c                   K   d }|j                   r*|j                   t        t        j                               z
  }| j                  j	                  | j                         ||       d {    y 7 w)Nr`   )client_secret_expires_atrg   rf   rA   rd   rP   )rG   client_inforb   s      r!   set_client_infoz#TokenStorageAdapter.set_client_info   sd     //66TYY[9IIC''++//1 , 
 	
 	
s   A(A2*A0+A2N)rH   r   rI   r=   )returnr=   rt   None)rt   zOAuthToken | None)rh   r   rt   rv   )rt   zfloat | None)rt   z!OAuthClientInformationFull | None)rr   r   rt   rv   )r*   r+   r,   __annotations__rJ   rN   rP   rR   rZ   r   r]   ri   rm   ro   rs   r&   r#   r!   r<   r<   S   s    ##55EE
 ,12
 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                   || _         || _        || _        || _        || _        t        |      | _        || _        |
| _        || _	        || _
        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)
            callback_host: Hostname used for OAuth redirect URI and callback server.
            callback_timeout: Seconds to wait for OAuth callback before timing out.
            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_portr"   _callback_host_callback_timeout_client_metadata_url
_client_id_client_secret_static_client_infor0   r1   httpx_client_factoryrz   _bind)rG   r6   scopesclient_nametoken_storageadditional_client_metadatacallback_portcallback_hostcallback_timeoutr   client_metadata_url	client_idclient_secrets                r!   rJ   zOAuth.__init__   s    T '++E(+?N!1$7!#+#' $8$ME<M<M!JJw r#   c           	        | j                   ry|j                  d      }| j                  xs t        | j                        | _        t        | j                        }d| 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       t3        ||      | _        || _        t8        	| u  ||| j4                  | j<                  | j>                  | j@                  | jB                         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/r   zhttp://r%   z	/callback  authorization_coderefresh_tokencode)r   redirect_urisgrant_typesresponse_typesscopeT)exclude_nonetoken_endpoint_auth_methodclient_secret_postnone)r   r   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)rH   rI   )rI   client_metadatastorageredirect_handlercallback_handlerr/   r   r&   )"rz   rstripr   r   r   redirect_portr'   
isinstancer|   listjoinr=   r   r}   r   r   r   
model_dumpr   r   r   r~   r	   warningsr   r<   token_storage_adapterr6   superrJ   r   r   r   r   )
rG   r6   redirect_hostredirect_uri
scopes_strr   metadatar   r   	__class__s
            r!   r   zOAuth._bind   s    ;;..%!00 
4G$$5
 6d6I6IJ q1C1C0DIN 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%Z 	 ;N)g;
" +..!22!22** $ 9 9 	 	
 r#   c                (  K   t         |           d{    | j                  H| j                  | j                  _        | j
                  j                  | j                         d{    | j                  j                  r| j                  j                  j                  rf| j
                  j                          d{   }||| j                  _
        y| j                  j                  | j                  j                         yyy7 7 7 Nw)zBLoad stored tokens and client info, properly setting token expiry.N)r   _initializer   contextrr   r   rs   current_tokensre   rm   token_expiry_timeupdate_token_expiry)rG   stored_expiryr   s     r!   r   zOAuth._initializeG  s     g!#####/'+'?'?DLL$,,<<T=U=UVVV<<&&4<<+F+F+Q+Q"&"<"<"M"M"OOM(1>.001L1LM ,R& 	$ W Ps6   DDAD)D*ADDADDD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: )	r   r2   r3   r)   RuntimeErrorloggerinfo
webbrowseropen)rG   authorization_urlr8   r9   s       r!   r   zOAuth.redirect_handlerV  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
                  | j                  ||      }t        j                         4 d{   }|j                  |j                         t        j                  d| j
                   d| j                          	 t        j                  | 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 57 # 1 sw Y   nxY wn+# t,        $ r}t-        d| j                   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    rI   result_containerresult_readyNu-   🎧 OAuth callback server started on http://r%   Tg?zOAuth callback timed out after z secondsz+OAuth callback handler could not be started)r   anyioEventr   r   r   r6   create_task_group
start_soonserver   r   
fail_afterr   waiterrorr   stateshould_exitsleepcancel_scopecancelTimeoutErrorr   )rG   resultr   servertges         r!   r   zOAuth.callback_handlerk  s     %&{{} 6##$$||#%
 **,,MM&,,'KK?@S@S?TTUVZVhVhUij)%%d&<&<=&++---||$ll*!;;4	 >= &*"kk#&&&&&(' -,, . '% - >==
   "5d6L6L5MXV > &*"kk#&&&&&( &*"kk#&&&&&(' -,,* HII+ -,,* HIIs   A%I8'E7(I8+AI7FF *E:+3F 	F'IE<I%I81E>2I8:F <I>I8 F		FG9	F5F00F55G99IG I9 H:H
 H::I=I8I	I8I5I" I5'I8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)rz   r   r   r   async_auth_flowasendStopAsyncIterationr)   r   r   debug_initializedr   rZ   )rG   requestgenr9   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 ClientNNN	localhostg     r@NNNN)r6   
str | Noner   zstr | list[str] | Noner   r=   r   zAsyncKeyValue | Noner   dict[str, Any] | Noner   z
int | Noner   r=   r   rk   r   zMcpHttpClientFactory | Noner   r   r   r   r   r   )r6   r=   rt   rv   ru   )r   r=   rt   rv   )rt   ztuple[str, str | None])r   zhttpx.Requestrt   z-AsyncGenerator[httpx.Request, httpx.Response])r*   r+   r,   r-   rw   rJ   r   r   r   r   r   __classcell__)r   s   @r!   r   r      s     L #)-+.2<@$(("'<@ +/ $$(!9 9  '9  	9 
 ,9  %:9  "9  9   9  :9  (9  9   "!9 vPdN+*%JN2$2	62 2r#   )r    r=   rt   r=   )N)r6   r=   r7   r   rt   ry   )2
__future__r   rf   r   collections.abcr   
contextlibr   typingr   r   r0   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   r"   r'   	Exceptionr)   r:   r<   r   r&   r#   r!   <module>r      s    "   *     ; 1 3 = 8 
   & ! 7 0)	H	L) L
 9= 5	<Z
, Z
zT Tr#   