
    ~hl9                       d dl mZ d dlZd dlZd dlZd dlmZ d dlmZm	Z	 d dl
mZ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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 dl!m"Z" d dl#m$Z$ dgZ% e$e&      Z'ddZ( G d de      Z) G d de      Z G d de      Z*	 d	 	 	 	 	 ddZ+	 d	 	 	 	 	 ddZ,	 	 	 	 d	 	 	 	 	 	 	 	 	 	 	 d dZ-y)!    )annotationsN)Path)AnyLiteral)urljoinurlparse)OAuthClientProvider)TokenStorage)OAuthClientInformationFullOAuthClientMetadata)OAuthMetadata)
OAuthToken)
AnyHttpUrlValidationError)settings)create_oauth_callback_server)find_available_port)
get_loggerOAuthc                 (    t         j                  dz  S )Nzoauth-mcp-client-cache)fastmcp_global_settingshome     U/opt/mcp/mcp-sentiment/venv/lib/python3.12/site-packages/fastmcp/client/auth/oauth.pydefault_cache_dirr   &   s    "''*BBBr   c                  Z    e Zd ZU dZdZded<   dZded<   dZded<   dgZded	<   dZ	ded
<   y)ServerOAuthMetadataz
    More flexible OAuth metadata model that accepts broader ranges of values
    than the restrictive MCP standard model.

    This handles real-world OAuth servers like PayPal that may support
    additional methods not in the MCP specification.
    Nzlist[str] | None code_challenge_methods_supported%token_endpoint_auth_methods_supportedgrant_types_supportedcodez	list[str]response_types_supportedresponse_modes_supported)
__name__
__module____qualname____doc__r   __annotations__r    r!   r#   r$   r   r   r   r   r   +   sO     :>$&6= ?C)+;B /3+2 ,2(i2 26.5r   r   c                       e Zd ZdZ	 	 	 	 ddZy)r	   zL
    OAuth client provider with more flexible OAuth metadata discovery.
    c                  K   | j                  |      }t        |d      }ddlm} d|i}t	        j
                         4 d{   }	 |j                  ||       d{   }|j                  dk(  r	 ddd      d{    y|j                          |j                         }t        j                  d|        t        j                  |      cddd      d{    S 7 7 7 g7 # t        $ r 	 |j                  |       d{  7  }|j                  dk(  rY ddd      d{  7   y|j                          |j                         }t        j                  d	|        t        j                  |      cY cddd      d{  7   S # t        $ r* t        j                  d
       Y Y ddd      d{  7   yw xY ww xY w# 1 d{  7  sw Y   yxY ww)z
        Discover OAuth metadata with flexible validation.

        This is nearly identical to the parent implementation but uses
        ServerOAuthMetadata instead of the restrictive MCP OAuthMetadata.
        '/.well-known/oauth-authorization-serverr   )LATEST_PROTOCOL_VERSIONzMCP-Protocol-VersionN)headers  zOAuth metadata discovered: z+OAuth metadata discovered (no MCP header): z!Failed to discover OAuth metadata)_get_authorization_base_urlr   	mcp.typesr-   httpxAsyncClientgetstatus_coderaise_for_statusjsonloggerdebugr   model_validate	Exception	exception)	self
server_urlauth_base_urlurlr-   r.   clientresponsemetadata_jsons	            r   _discover_oauth_metadataz,OAuthClientProvider._discover_oauth_metadataI   s     88Dm%NO5)+BC$$& 	  	 & !'C!AA''3.		  	  	 
 ))+ (:=/JK*99-H	  	  	 A	  	     %+ZZ_44H++s2#	  	  	  --/$,MMOMLLEm_U /==mLL)	  	  	 * !  $$%HI/	  	  	 *  	  	  	 s
  A G C"G GC*C$C*3G >C&?G AC*G C(G $C*&G (G *	G4FD	FGG )D,*G 0AF<G=G>G 
FG G/G0G1G <F?=G GGGGGGG N)r>   strreturnzServerOAuthMetadata | None)r%   r&   r'   r(   rD   r   r   r   r	   r	   D   s    ( ( 	#( r   r	   c                  x    e Zd ZdZdddZedd       ZddZddZddZ	ddZ
dd	Zdd
ZddZeddd       Zy)FileTokenStoragez
    File-based token storage implementation for OAuth credentials and tokens.
    Implements the mcp.client.auth.TokenStorage protocol.

    Each instance is tied to a specific server URL for proper token isolation.
    Nc                r    || _         |xs
 t               | _        | j                  j                  dd       y)z-Initialize storage for a specific server URL.T)exist_okparentsN)r>   r   	cache_dirmkdir)r=   r>   rL   s      r   __init__zFileTokenStorage.__init__|   s0    $"9&7&9dD9r   c                N    t        |       }|j                   d|j                   S )z0Extract the base URL (scheme + host) from a URL.://)r   schemenetloc)r@   parseds     r   get_base_urlzFileTokenStorage.get_base_url   s&     #--FMM?33r   c                    | j                  | j                        }|j                  dd      j                  dd      j                  dd      j                  dd      S )z:Generate a safe filesystem key from the server's base URL.rP   _./:)rT   r>   replace)r=   base_urls     r   get_cache_keyzFileTokenStorage.get_cache_key   sO    $$T__5UC(WS#WS#WS#		
r   c                L    | j                         }| j                  | d| dz  S )z4Get the file path for the specified cache file type.rV   .json)r\   rL   )r=   	file_typekeys      r   _get_file_pathzFileTokenStorage._get_file_path   s,      "~~3%q5 999r   c                6  K   | j                  d      }	 t        j                  |j                               }|S # t        t
        j                  t        f$ r>}t        j                  d| j                  | j                         d|        Y d}~yd}~ww xY ww)zLoad tokens from file storage.tokenszCould not load tokens for : N)ra   r   model_validate_json	read_textFileNotFoundErrorr7   JSONDecodeErrorr   r8   r9   rT   r>   )r=   pathrc   es       r   
get_tokenszFileTokenStorage.get_tokens   s     ""8,	33DNN4DEF
 M!4#7#7I 	LL,T->->t-O,PPRSTRUV 		s+   B$: BB4BBBBc                   K   | j                  d      }|j                  |j                  d             t        j	                  d| j                  | j                                yw)zSave tokens to file storage.rc      indentzSaved tokens for Nra   
write_textmodel_dump_jsonr8   r9   rT   r>   )r=   rc   ri   s      r   
set_tokenszFileTokenStorage.set_tokens   sT     ""8,..a.89():):4??)K(LMN   A%A'c                  K   | j                  d      }	 t        j                  |j                               }| j	                          d{   }|Vt
        j                  d| j                  | j                         d       | j                  d      }|j                  d       y|S 7 ^# t        t        j                  t        f$ r>}t
        j                  d| j                  | j                         d|        Y d}~yd}~ww xY ww)	z*Load client information from file storage.client_infoNz#No tokens found for client info at zX. OAuth flow may have been incomplete. Clearing client info to force fresh registration.T
missing_okzCould not load client info for rd   )ra   r   re   rf   rk   r8   r9   rT   r>   unlinkrg   r7   rh   r   )r=   ri   rv   rc   client_info_pathrj   s         r   get_client_infoz FileTokenStorage.get_client_info   s     ""=1	4HH K  ??,,F~9$:K:KDOO:\9] ^m m
 $(#6#6}#E  ''4'8 - "4#7#7I 	LL1$2C2CDOO2T1UUWXYWZ[ 		sM   D6B, B*AB, 'D(B, )D*B, ,D
4D>DDDc                   K   | j                  d      }|j                  |j                  d             t        j	                  d| j                  | j                                yw)z(Save client information to file storage.rv   rm   rn   zSaved client info for Nrp   )r=   rv   ri   s      r   set_client_infoz FileTokenStorage.set_client_info   sT     ""=13313=>-d.?.?.P-QRSrt   c                    ddg}|D ]%  }| j                  |      }|j                  d       ' t        j                  d| j	                  | j
                                y)z&Clear all cached data for this server.rv   rc   Trw   zCleared OAuth cache for N)ra   ry   r8   inforT   r>   )r=   
file_typesr_   ri   s       r   clearzFileTokenStorage.clear   s_    >KX=V
# 	)I&&y1DKK4K(	) 	.t/@/@/Q.RSTr   c                    |xs
 t               }|j                         syddg}|D ].  }|j                  d| d      D ]  }|j                  d        0 t        j                  d       y)	z&Clear all cached data for all servers.Nrv   rc   z*_r^   Trw   z$Cleared all OAuth client cache data.)r   existsglobry   r8   r   )clsrL   r   r_   files        r   	clear_allzFileTokenStorage.clear_all   sx     4!2!4	!>KX=V
# 	-I!I;e'<= -t,-	- 	:;r   N)r>   rE   rL   Path | None)r@   rE   rF   rE   )rF   rE   )r_   z Literal['client_info', 'tokens']rF   r   )rF   zOAuthToken | None)rc   r   rF   None)rF   z!OAuthClientInformationFull | None)rv   r   rF   r   )rF   r   )rL   r   rF   r   )r%   r&   r'   r(   rN   staticmethodrT   r\   ra   rk   rs   r{   r}   r   classmethodr   r   r   r   rH   rH   t   sZ    : 4 4

:
"O8TU 
< 
<r   rH   c                L  K   t        | d      }t        j                  d|        t        j                  di |xs i 4 d{   }	 |j                  |d       d{   }|j                  dk(  rIt        j                  d       t        j                  |j                               cddd      d{    S |j                  dk(  r't        j                  d	       	 ddd      d{    yt        j                  d
|j                          	 ddd      d{    y7 7 7 s7 ?7 # t        j                  t        j                  t        f$ r3}t        j                  d|        Y d}~ddd      d{  7   yd}~ww xY w# 1 d{  7  sw Y   yxY ww)a+  
    Discover OAuth metadata from the server using RFC 8414 well-known endpoint.

    Args:
        server_base_url: Base URL of the OAuth server (e.g., "https://example.com")
        httpx_kwargs: Additional kwargs for httpx client

    Returns:
        OAuth metadata if found, None otherwise
    r,   z!Discovering OAuth metadata from: Ng      $@timeout   z&Successfully discovered OAuth metadatar/   z<OAuth metadata not found (404) - server may not require authzOAuth metadata request failed: z!OAuth metadata discovery failed: r   )r   r8   r9   r2   r3   r4   r5   _MCPServerOAuthMetadatar:   r7   warningRequestErrorrh   r   )server_base_urlhttpx_kwargswell_known_urlrA   rB   rj   s         r   discover_oauth_metadatar      sv     _.WXN
LL4^4DEF  8L$6B8  F	#ZZZEEH##s*EF.==hmmoN   %%,R    !@AUAU@VWX  E   ""D$8$8/J 	LL<QC@A!  	  s   AF$D'F$F
D1 D)!A
D1+F$7D+8F$=$D1"F$-D-.F$3"D1F$!D/"F$)D1+F$-F$/F$1(FF1F5F$ FF$FFF!FF!F$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      @r   )i  i  TzWWW-AuthenticateFr   )r2   r3   r4   r5   r.   r   )mcp_urlr   rA   rB   s       r   check_if_auth_requiredr   	  s        8L$6B8  F	#ZZZ==H ##z1   "X%5%55      >    !! 	%   	!  s   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           
     N   t        |       }|j                   d|j                   t               d d}t	        |t
              rdj                  |      }t        d|t        |      gddgdgd|d	|xs i }t        |
      }dd}	dfd}
t        |||	|
      }|S )a}  
    Create an OAuthClientProvider for an MCP server.

    This is intended to be provided to the `auth` parameter of an
    httpx.AsyncClient (or appropriate FastMCP client/transport instance)

    Args:
        mcp_url: Full URL to the MCP endpoint (e.g.,
        "http://host/mcp/sse")
        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_cache_dir: Directory for FileTokenStorage
        additional_client_metadata: Extra fields for OAuthClientMetadata

    Returns:
        OAuthClientProvider
    rP   zhttp://127.0.0.1:z	/callback authorization_coderefresh_tokenr"   client_secret_post)client_nameredirect_urisgrant_typesresponse_typestoken_endpoint_auth_methodscope)r>   rL   c                f   K   t         j                  d|         t        j                  |        yw)zOpen browser for authorization.zOAuth authorization URL: N)r8   r   
webbrowseropen)authorization_urls    r   redirect_handlerzOAuth.<locals>.redirect_handlerZ  s)     /0A/BCD)*s   /1c                   K   t        j                         j                         } t        |       }t	        j
                         4 d{   }|j                  |j                         t        j                  d        d}	 t	        j                  |      5  |  d{   \  }}||fcddd       d|_        t        j                  d       d{    |j                  j                          cddd      d{    S 7 7 e7 47 # 1 sw Y   nxY wn# t        $ r t        d| d      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   y# 1 d{  7  sw Y   yxY ww)	z4Handle OAuth callback and return (auth_code, state).)portr>   response_futureNu7   🎧 OAuth callback server started on http://127.0.0.1:g     r@Tg?zOAuth callback timed out after z seconds)asyncioget_running_loopcreate_futurer   anyiocreate_task_group
start_soonserver8   r   
fail_aftershould_exitsleepcancel_scopecancelTimeoutError)r   servertgTIMEOUT	auth_codestateredirect_portr   s         r   callback_handlerzOAuth.<locals>.callback_handler_  s     "224BBD .&+
 **, 	) 	)MM&,,'KKI-Y G	)%%g. ,-<'<$Iu$e+, , &*"mmC(((&&(!	) 	) 	) (= )	), , ,   X"%DWIX#VWWX, &*"mmC(((&&( &*"mmC(((&&(!	) 	) 	) 	) 	)s   A	G#DG#6GDD"D#
D-	D6GD
G4G# DG#D
GG#D	DE8D44E88GE G8 F9F
 F99G<G#G
G#G GG G#)r>   client_metadatastorager   r   r   )r   rE   rF   r   )rF   ztuple[str, str | None])r   rQ   rR   r   
isinstancelistjoinr   r   rH   r	   )r   scopesr   token_storage_cache_diradditional_client_metadata
parsed_urlredirect_urir   r   r   r   oauth_providerr   r   s               @@r   r   r   '  s    2 '"J#**+3z/@/@.ABO ()M&}oY?L&$&!) !,/0)?;x#7 &+O ".EG
+
)@ )"'))N r   )rF   r   r   )r   rE   r   dict[str, Any] | NonerF   z_MCPServerOAuthMetadata | None)r   rE   r   r   rF   bool)NzFastMCP ClientNN)r   rE   r   zstr | list[str] | Noner   rE   r   r   r   r   rF   _MCPOAuthClientProvider).
__future__r   r   r7   r   pathlibr   typingr   r   urllib.parser   r   r   r2   mcp.client.authr	   r   r
   mcp.shared.authr   r   r   r   r   pydanticr   r   fastmcpr   r   fastmcp.client.oauth_callbackr   fastmcp.utilities.httpr   fastmcp.utilities.loggingr   __all__r%   r8   r   r   rH   r   r   r   r   r   r   <module>r      s%   "      *   J ( 1 7 7 0)	H	C
61 62- 1 - `o<| o<f AE  (= # H 9= 5	@ &*'+/8<``"` ` )	`
 !6` `r   