
    kKj                    
   U d Z ddlmZ ddlZddlZddlZddlmZmZm	Z	 ddl
mZmZmZ ddl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mZ ddl m!Z! ddl"m#Z#m$Z$ ddl%m&Z&m'Z' ddl(m)Z)m*Z* ddl+m,Z,m-Z- ddl.m/Z/ ddl0m1Z1 ddl2m3Z3 ddl4m5Z5 ddl6m7Z7 ddl8m9Z9m:Z:m;Z; ddl<m=Z= ddl>m?Z?m@Z@ ddlAmBZBmCZC ddlDmEZE ddlFmGZG ddlHmIZI ddlJmKZKmLZLmMZM ddlNmOZO ddlPmQZQ dd lRmSZS dd!lTmUZU dd"lVmWZWmXZX dd#lYmZZZm[Z[ dd$l\m]Z] dd%l^m_Z_m`Z` erdd&lambZb dd'l2mcZc  e]ed      Zeeg e#f   eg ee#   f   z  ZfdHd(Zg G d) d*eL      Zh G d+ d,eW      Zi G d- d.e?      Zj G d/ d0e@      Zk G d1 d2e:      Zl G d3 d4      Zmd5Znd6eod7<    G d8 d9eQ      Zp	 	 	 	 dId:Zq G d; d<eS      Zr	 	 	 	 dJd=Zs	 	 	 	 	 	 	 	 dKd>Zt	 	 	 	 	 	 	 	 	 	 dLd?ZudMd@Zv	 	 	 	 	 	 	 	 dNdAZw	 	 	 	 dOdBZxdPdCZy G dD dEe#e3         Zz G dF dGeze3         Z{y)QzProxyProvider for proxying to remote MCP servers.

This module provides the `ProxyProvider` class that proxies components from
a remote MCP server via a client factory. It also provides proxy component
classes that forward execution to remote servers.
    )annotationsN)	AwaitableCallableSequence)TYPE_CHECKINGAnycast)ServerSession)ClientSession)request_ctx)LifespanContextTRequestContext)McpError)METHOD_NOT_FOUNDBlobResourceContentsElicitRequestFormParamsTextResourceContents)AnyUrl)ClientFastMCP1Server)ElicitResultcreate_elicitation_callback)
LogMessagecreate_log_callback)	RootsListcreate_roots_callback)create_sampling_callback)client_span)ClientTransportT)ResourceError)	MCPConfig)MessagePromptPromptResult)PromptArgument)ResourceResourceTemplate)ResourceContentResourceResult)expand_uri_template)Context)get_context)CallNext
MiddlewareMiddlewareContext)ProviderErrorStrategy)Provider)FastMCP)
TaskConfig)Tool
ToolResult)FastMCPComponentget_fastmcp_metadata)
get_logger)VersionSpecversion_sort_key)Path)ClientTransportc                    t        t        j                  j                  t        j                  j                  t        |                   S )N)codemessage)r   mcptypes	ErrorDataINTERNAL_ERRORstr)errors    o/Users/ahmed/devFolder/Ultron/claude-voice/.venv/lib/python3.12/site-packages/fastmcp/server/providers/proxy.py_proxy_upstream_errorrG   E   s:    		))J 	 	
     c                  (    e Zd ZddZ	 	 	 	 	 	 ddZy)ProxyInitializeMiddlewarec                    || _         y N)proxy)selfrM   s     rF   __init__z"ProxyInitializeMiddleware.__init__O   s	    
rH   c                j  K   | j                   j                          d {   }	 t        |t              r3|j                  }|%|j
                  |j                  f|j                  d<   |4 d {    |j                          d {    d d d       d {     ||       d {   S 7 7 >7 (7 # 1 d {  7  sw Y   *xY w# t        $ r  t        t        t        j                  t        j                  t        j                   t        j"                  f$ r}t%        |      |d }~ww xY w7 w)Nr   )rM   _get_client
isinstanceStatefulProxyClientfastmcp_contextrequest_context_fastmcp_proxy_rc_ref
initializer   RuntimeErrorTimeoutErrorhttpx	HTTPErroranyioClosedResourceErrorEndOfStreamBrokenResourceErrorrG   )rN   context	call_nextclientctxrE   s         rF   on_initializez'ProxyInitializeMiddleware.on_initializeR   s     zz--//	:&"56--?++/F((+ v''))) v w'''/ 0 ) vvv 	OO%%%%
 	: (.E9	: (s   D3B,D3A	C	 /B.0C	 3B4B0B4C	 B2C	 D3'D1(D3.C	 0B42C	 4C:B=;CC	 	AD.D))D..D3N)rM   FastMCPProxyreturnNone)ra   z.MiddlewareContext[mcp.types.InitializeRequest]rb   zHCallNext[mcp.types.InitializeRequest, mcp.types.InitializeResult | None]rg   z!mcp.types.InitializeResult | None)__name__
__module____qualname__rO   re    rH   rF   rJ   rJ   N   s(    (?(
( 
+(rH   rJ   c                       e Zd ZU dZ ed      Zded<   dZded<   d fd	Zdd
Z	d fdZ
e	 	 	 	 	 	 dd       Z	 d	 	 	 	 	 ddZd fdZ xZS )	ProxyToolz>A Tool that represents and executes a tool on a remote server.	forbiddenmoder3   task_configN
str | None_backend_namec                2    t        |   di | || _        y Nrl   superrO   _client_factoryrN   client_factorykwargs	__class__s      rF   rO   zProxyTool.__init__       "6"-rH   c                   K   | j                         }t        j                  |      rt        t        | d{         }|S 7 
wz<Gets a client instance by calling the sync or async factory.Nry   inspectisawaitabler	   r   rN   rc   s     rF   rQ   zProxyTool._get_client   :     %%'v&&,/F #/   5AA
Ac                    |j                  di       }d|v r#| j                  i |d| j                  i}||d<   t        |   di |S z5Override to preserve _backend_name when name changes.updatenamert   rl   getrt   r   rx   
model_copyrN   r|   r   r}   s      rF   r   zProxyTool.model_copy   X    Hb)V 2 2 :;;;F%F8w!+F++rH   c                "    | ||j                   |j                  |j                  |j                  |j                  |j
                  |j                  |j                  t        |j                        j                  dg       |j                        S )z@Factory method to create a ProxyTool from a raw MCP tool schema.tags)r{   r   titledescription
parametersr   output_schemaiconsmetar   	execution)r   r   r   inputSchemar   outputSchemar   r   r7   r   r   )clsr{   mcp_tools      rF   from_mcp_toolzProxyTool.from_mcp_tool   sv    
 ).. ,,++ ,,"//..%hmm488D((
 	
rH   c                  K   | j                   xs | j                  }t        d| d||      5 }|j                  dd       | j	                          d{   }|4 d{    |xs
 t               }t        |t              r%|j                  |j                  f|j                  d<   d}t        |d      r|j                  }t        |d	      r!|j                  rt        |j                        }t        |d
      r_t        |j                  d      rI|j                  j                  r3|j                  j                   }	|	r|xs i }|	j#                  d      |d<   |j%                  |||       d{   }
ddd      d{    t'        
j(                  |
j*                  |
j                  |
j,                        cddd       S 7 q7 i7 Z7 L# 1 d{  7  sw Y   \xY w# 1 sw Y   yxY ww)z6Executes the tool by making a call through the client.ztools/call z
tools/call)	tool_namefastmcp.provider.typeProxyProviderNr   rU   r   experimentalis_taskT)exclude_nonezmodelcontextprotocol.io/task)r   	argumentsr   )contentstructured_contentr   is_error)rt   r   r   set_attributerQ   r,   rR   rS   rU   rV   rW   hasattrr   dictr   r   task_metadata
model_dumpcall_tool_mcpr5   r   structuredContentisError)rN   r   ra   backend_namespanrc   rd   r   req_ctxr   results              rF   runzProxyTool.run   s     ))6TYY,("	

 6H++--Fv.
 f&9:++/F((+
 /33 12!11Gw/GLL#GLL1  8#G$8$8)D#0088(/(<(<(J(J(#':2D - 8 8d 8 K !!?@  &33%  4   ? vN #)#;#;[[	_
 
 .>? vvv
 
s   -G1&G%G
G% G	!G%$DG1G2G6G%G:G%<
G1G%	G%GG%G"	GG"	G%%G.*G1c                \    t         |          d| j                  xs | j                  dz  S Nr   )r   zfastmcp.proxy.backend_namerx   get_span_attributesrt   r   rN   r}   s    rF   r   zProxyTool.get_span_attributes   2    w*,%4*.*<*<*I		0
 
 	
rH   r{   ClientFactoryTr|   r   rg   r   )r|   r   rg   rn   )r{   r   r   zmcp.types.Toolrg   rn   rL   )r   dict[str, Any]ra   Context | Nonerg   r5   rg   r   )ri   rj   rk   __doc__r3   rr   __annotations__rt   rO   rQ   r   classmethodr   r   r   __classcell__r}   s   @rF   rn   rn   y   s    H(k:K: $M:$., 
+
7E
	
 
* #';!;  ; 
	;z
 
rH   rn   c                       e Zd ZU dZ ed      Zded<   dZded<   dZd	ed
<   dd	 	 	 	 	 d fdZ	ddZ
d fdZe	 	 	 	 	 	 dd       ZddZd fdZ xZS )ProxyResourcezEA Resource that represents and reads a resource from a remote server.ro   rp   r3   rr   NResourceResult | None_cached_contentrs   _backend_uri)r   c               @    t        |   di | || _        || _        y rv   )rx   rO   ry   r   )rN   r{   r   r|   r}   s       rF   rO   zProxyResource.__init__   s%     	"6"-.rH   c                   K   | j                         }t        j                  |      rt        t        | d{         }|S 7 
wr   r   r   s     rF   rQ   zProxyResource._get_client   r   r   c                    |j                  di       }d|v r,| j                   i |dt        | j                        i}||d<   t	        |   di |S )z3Override to preserve _backend_uri when uri changes.r   urir   rl   )r   r   rD   r   rx   r   r   s      rF   r   zProxyResource.model_copy  s[    Hb)F?t008>>DHH>F%F8w!+F++rH   c                    | ||j                   |j                  |j                  |j                  |j                  xs d|j
                  |j                  t        |j                        j                  dg       t        d      
      S )zHFactory method to create a ProxyResource from a raw MCP resource schema.
text/plainr   ro   rp   )
r{   r   r   r   r   	mime_typer   r   r   rr   )
r   r   r   r   mimeTyper   r   r7   r   r3   )r   r{   mcp_resources      rF   from_mcp_resourcezProxyResource.from_mcp_resource  s{     )  ""$$$00"++;|$$""%l&7&78<<VRH"4
 	
rH   c           	       K   | j                   | j                   S | j                  xs t        | j                        }t	        dd||      5 }|j                  dd       | j                          d{   }|4 d{    |j                  |       d{   }ddd      d{    st        d|       g }|D ]  }t        |t              r<|j                  t        |j                  |j                  |j                               Ot        |t               rO|j                  t        t#        j$                  |j&                        |j                  |j                               t        dt)        |              t+        |	      cddd       S 7 (7  7 
7 # 1 d{  7  sw Y   xY w# 1 sw Y   yxY ww)
z1Read the resource content from the remote server.Nzresources/read)resource_urir   r   )Remote server returned empty content for r   r   r   Unsupported content type: contents)r   r   rD   r   r   r   rQ   read_resourcer    rR   r   appendr(   textr   r   r   base64	b64decodeblobtyper)   )rN   backend_urir   rc   r   r   items          rF   readzProxyResource.read!  s    +'''''83txx=$	

 6H++--Fv%33K@@ v#?}M 
 /1Hd$89OO'$(II&*mm!%  &:;OO'$*$4$4TYY$?&*mm!% (*DT$ZL(QRR% ( "84K
 
 .@ vvv
 
s   AG&F<3F4
F<>F?F<F&F!F&F<'F$(C)F<
GF<F<!F&$F<&F9	,F/-F9	4F<<GGc                n    t         |          d| j                  xs t        | j                        dz  S )Nr   )r   zfastmcp.proxy.backend_uri)rx   r   r   rD   r   r   s    rF   r   z!ProxyResource.get_span_attributesN  s6    w*,%4)-):):)Kc$((m0
 
 	
rH   )r{   r   r   r   r|   r   r   )r|   r   rg   r   )r{   r   r   zmcp.types.Resourcerg   r   )rg   r)   r   )ri   rj   rk   r   r3   rr   r   r   r   rO   rQ   r   r   r   r   r   r   r   s   @rF   r   r      s    O(k:K:-1O*1#L*# 26		/&	/ /		/
 	/, 
&
 )
 
	
 
(+5Z
 
rH   r   c                       e Zd ZU dZ ed      Zded<   dZded<   d fd	Zdd
Z	d fdZ
e	 	 	 	 	 	 dd       Z	 d	 	 	 	 	 	 	 ddZd fdZ xZS )ProxyTemplatezWA ResourceTemplate that represents and creates resources from a remote server template.ro   rp   r3   rr   Nrs   _backend_uri_templatec                2    t        |   di | || _        y rv   rw   rz   s      rF   rO   zProxyTemplate.__init__[  r~   rH   c                   K   | j                         }t        j                  |      rt        t        | d{         }|S 7 
wr   r   r   s     rF   rQ   zProxyTemplate._get_client_  r   r   c                    |j                  di       }d|v r#| j                  i |d| j                  i}||d<   t        |   di |S )zEOverride to preserve _backend_uri_template when uri_template changes.r   uri_templater   rl   )r   r   r   rx   r   r   s      rF   r   zProxyTemplate.model_copyf  s[    Hb)V#(B(B(JKK 79J9JKF%F8w!+F++rH   c                    | ||j                   |j                  |j                  |j                  |j                  xs d|j
                  i |j                  t        |j                        j                  dg       t        d            S )zHFactory method to create a ProxyTemplate from a raw MCP template schema.r   r   ro   rp   )r{   r   r   r   r   r   r   r   r   r   rr   )
uriTemplater   r   r   r   r   r   r7   r   r3   )r   r{   mcp_templates      rF   from_mcp_templatezProxyTemplate.from_mcp_templateo  s~     )%11""$$$00"++;|$$""%l&7&78<<VRH"4
 	
rH   c                   K   | j                   xs | j                  }t        ||      }| j                          d{   }|4 d{    |j	                  |       d{   }ddd      d{    st        d|       g }|D ]  }	t        |	t              r<|j                  t        |	j                  |	j                  |	j                               Ot        |	t              rO|j                  t        t        j                  |	j                         |	j                  |	j                               t        dt#        |	              t%        |      }
t'        | j(                  || j*                  | j,                  | j.                  |d   j                  | j0                  | j                  t3        | j                        j5                  dg       |

      S 7 7 7 7 u# 1 d{  7  sw Y   xY ww)	zACreate a resource from the template by calling the remote server.Nr   r   r   r   r   r   )
r{   r   r   r   r   r   r   r   r   r   )r   r   r*   rQ   r   r    rR   r   r   r(   r   r   r   r   r   r   r   r   r)   r   ry   r   r   r   r   r7   r   )rN   r   paramsra   backend_templateparameterized_urirc   r   r   r   cached_contents              rF   create_resourcezProxyTemplate.create_resource  s      55J9J9J/0@&I''))6!//0ABBF 6 ;<M;NO 
 +-D$ 45# $		"&--!YY D"67# & 0 0 ;"&--!YY $&@d$MNN% ( (://!**((h**%dii044VR@*
 	
C *B 666sj   :G>G
G>GG>G( G"!G(%G>0G%1E,G>G>"G(%G>(G;.G1/G;6G>c                \    t         |          d| j                  xs | j                  dz  S )Nr   )r   z"fastmcp.proxy.backend_uri_template)rx   r   r   r   r   s    rF   r   z!ProxyTemplate.get_span_attributes  s4    w*,%4**?d.?.?0
 
 	
rH   r   r   )r|   r   rg   r   )r{   r   r   zmcp.types.ResourceTemplaterg   r   rL   )r   rD   r   r   ra   r   rg   r   r   )ri   rj   rk   r   r3   rr   r   r   rO   rQ   r   r   r   r   r   r   r   s   @rF   r   r   U  s    a(k:K:(,:,., 
+
;U
	
 
. #'	:
:
 :
  	:

 
:
x
 
rH   r   c                       e Zd ZU dZ ed      Zded<   dZded<   d fd	Zdd
Z	d fdZ
e	 	 	 	 	 	 dd       ZddZd fdZ xZS )ProxyPromptzCA Prompt that represents and renders a prompt from a remote server.ro   rp   r3   rr   Nrs   rt   c                2    t        |   di | || _        y rv   rw   rz   s      rF   rO   zProxyPrompt.__init__  r~   rH   c                   K   | j                         }t        j                  |      rt        t        | d{         }|S 7 
wr   r   r   s     rF   rQ   zProxyPrompt._get_client  r   r   c                    |j                  di       }d|v r#| j                  i |d| j                  i}||d<   t        |   di |S r   r   r   s      rF   r   zProxyPrompt.model_copy  r   rH   c                   |j                   xs g D cg c]2  }t        |j                  |j                  |j                  xs d      4 }} | ||j                  |j
                  |j                  ||j                  |j                  t        |j                        j                  dg       t        d      	      S c c}w )zDFactory method to create a ProxyPrompt from a raw MCP prompt schema.F)r   r   requiredr   ro   rp   )	r{   r   r   r   r   r   r   r   rr   )r   r%   r   r   r   r   r   r   r7   r   r3   )r   r{   
mcp_promptargr   s        rF   from_mcp_promptzProxyPrompt.from_mcp_prompt  s     "++1r1
 2 XXOO.
 2 	 
 )"""..""%joo6::62F"4

 
	

s   7B<c                D  K   | j                   xs | j                  }t        d| d||      5 }|j                  dd       | j	                          d{   }|4 d{    |j                  ||       d{   }ddd      d{    j                  D cg c]#  }t        |j                  |j                        % }}t        ||j                  |j                        cddd       S 7 7 7 {7 m# 1 d{  7  sw Y   }xY wc c}w # 1 sw Y   yxY ww)	z6Render the prompt by making a call through the client.zprompts/get zprompts/get)prompt_namer   r   N)r   role)messagesr   r   )rt   r   r   r   rQ   
get_promptr  r"   r   r  r$   r   r   )rN   r   r   r   rc   r   mr  s           rF   renderzProxyPrompt.render  s     ))6TYY<.)$	

 6H++--Fv%00yII v BHAPA		7    !"..[[!
 
 .I vvv

 
s   -D &DC2
D C4!D$C::C6;C:?D
C8D(D#D(
D 2D4D6C:8D:D	 DD	DDD c                \    t         |          d| j                  xs | j                  dz  S r   r   r   s    rF   r   zProxyPrompt.get_span_attributes  r   rH   )r{   r   r   )r|   r   rg   r   )r{   r   r  zmcp.types.Promptrg   r   )r   r   rg   r$   r   )ri   rj   rk   r   r3   rr   r   rt   rO   rQ   r   r   r  r
  r   r   r   s   @rF   r   r     si    M(k:K: $M:$., 
+
9I
	
 
02
 
rH   r   c                  $    e Zd ZdZdZddZddZy)_CacheEntryz;A cached sequence of components with a monotonic timestamp.items	timestampc                     || _         || _        y rL   r  )rN   r  r  s      rF   rO   z_CacheEntry.__init__%  s    
"rH   c                J    t        j                         | j                  z
  |k  S rL   )time	monotonicr  )rN   ttls     rF   is_freshz_CacheEntry.is_fresh)  s     4>>1S88rH   N)r  zSequence[Any]r  float)r  r  rg   bool)ri   rj   rk   r   	__slots__rO   r  rl   rH   rF   r  r     s    E&I#9rH   r  g     r@r  _DEFAULT_CACHE_TTLc                       e Zd ZdZ	 d	 	 	 d fdZddZddZ	 d	 	 	 	 	 ddZddZ	 d	 	 	 	 	 ddZ	ddZ
	 d	 	 	 	 	 dd	Zdd
Z	 d	 	 	 	 	 ddZddZ xZS )r   a  Provider that proxies to a remote MCP server via a client factory.

    This provider fetches components from a remote server and returns Proxy*
    component instances that forward execution to the remote server.

    All components returned by this provider have task_config.mode="forbidden"
    because tasks cannot be executed through a proxy.

    Component lists (tools, resources, templates, prompts) are cached so that
    individual lookups (e.g. during ``call_tool``) can resolve from the cache
    instead of opening a new backend connection.  The cache stores the
    backend's raw component metadata and is shared across all sessions;
    per-session visibility and auth filtering are applied after cache lookup
    by the server layer.  The cache is refreshed whenever a ``list_*`` call
    is made, and entries expire after ``cache_ttl`` seconds (default 300).
    Set ``cache_ttl=0`` to disable caching.  Disabling is recommended for
    backends whose component lists change dynamically.

    Example:
        ```python
        from fastmcp import FastMCP
        from fastmcp.server.providers.proxy import ProxyProvider, ProxyClient

        # Create a proxy provider for a remote server
        proxy = ProxyProvider(lambda: ProxyClient("http://localhost:8000/mcp"))

        mcp = FastMCP("Proxy Server")
        mcp.add_provider(proxy)

        # Can also add with namespace
        mcp.add_provider(proxy.with_namespace("remote"))
        ```
    c                    t         |           || _        ||nt        | _        d| _        d| _        d| _        d| _        y)a  Initialize a ProxyProvider.

        Args:
            client_factory: A callable that returns a Client instance when called.
                           This gives you full control over session creation and reuse.
                           Can be either a synchronous or asynchronous function.
            cache_ttl: How long (in seconds) to cache component lists for
                      individual lookups.  Defaults to 300.  Set to 0 to
                      disable caching.
        N)	rx   rO   r{   r  
_cache_ttl_tools_cache_resources_cache_templates_cache_prompts_cache)rN   r{   	cache_ttlr}   s      rF   rO   zProxyProvider.__init__S  sG     	,'0'<)BT6:>BFJ:>rH   c                   K   | j                         }t        j                  |      rt        t        | d{         }|S 7 
wr   r{   r   r   r	   r   r   s     rF   rQ   zProxyProvider._get_clientj  s:     $$&v&&,/F #/r   c                  K   	 | j                          d{   }|4 d{    |j                          d{   }|D cg c]"  }t        j                  | j                  |      $ }}ddd      d{    t        t        j                               | _        |S 7 7 7 jc c}w 7 4# 1 d{  7  sw Y   DxY w# t
        $ r+}|j                  j                  t        k(  rg }n Y d}~vd}~ww xY ww)z&List all tools from the remote server.N)rQ   
list_toolsrn   r   r{   r   rE   r>   r   r  r  r  r  )rN   rc   	mcp_toolsttoolses         rF   _list_toolszProxyProvider._list_toolsu  s     	++--Fv"("3"3"55	MVMVI++D,?,?CY   v (t~~/?@ .5 vvv
  	ww||// 	   C:C B!
C B#C B.B%B.'B'*B.,C 7B,8C <%C:!C #C %B.'B.,C .C 4B75C <C 	C7!C2-C:2C77C:c                  K   | j                   }||j                  | j                        s$| j                          d {    | j                   }|J |j                  D cg c]  }|j
                  |k(  s| }}|r+|D cg c]   }|j                  |j                        s|" }}|sy t        |t              S 7 ~c c}w c c}w wN)key)
r  r  r  r+  r  r   matchesversionmaxr:   )rN   r   r1  cacher(  matchings         rF   	_get_toolzProxyProvider._get_tool  s      !!=t ?""$$$%%E   ${{={!affnA{=#+J8awqyy/I8HJ8!122 % >J:   =C
B> "C
"C 7C ;C
 C$C(C
 
C
c                  K   	 | j                          d{   }|4 d{    |j                          d{   }|D cg c]"  }t        j                  | j                  |      $ }}ddd      d{    t        t        j                               | _        |S 7 7 7 jc c}w 7 4# 1 d{  7  sw Y   DxY w# t
        $ r+}|j                  j                  t        k(  rg }n Y d}~vd}~ww xY ww)z*List all resources from the remote server.N)rQ   list_resourcesr   r   r{   r   rE   r>   r   r  r  r  r  )rN   rc   mcp_resourcesr	resourcesr*  s         rF   _list_resourceszProxyProvider._list_resources  s     	++--Fv&,&;&;&= = +* "33D4G4GK*   v !,It~~7G H . = vvv  	ww||//	 	r,  c                  K   | j                   }||j                  | j                        s$| j                          d {    | j                   }|J |j                  D cg c]  }t        |j                        |k(  s| }}|r+|D cg c]   }|j                  |j                        s|" }}|sy t        |t              S 7 c c}w c c}w wr.  )r  r  r  r<  r  rD   r   r0  r1  r2  r:   )rN   r   r1  r3  r:  r4  s         rF   _get_resourcezProxyProvider._get_resource  s      %%=t ?&&((())E   ${{@{!c!%%jC.?A{@#+J8awqyy/I8HJ8!122 ) AJs:   =CC "C"C	 C	C C-C1C	
Cc                  K   	 | j                          d{   }|4 d{    |j                          d{   }|D cg c]"  }t        j                  | j                  |      $ }}ddd      d{    t        t        j                               | _        |S 7 7 7 jc c}w 7 4# 1 d{  7  sw Y   DxY w# t
        $ r+}|j                  j                  t        k(  rg }n Y d}~vd}~ww xY ww)z3List all resource templates from the remote server.N)rQ   list_resource_templatesr   r   r{   r   rE   r>   r   r  r  r  r   )rN   rc   mcp_templatesr(  	templatesr*  s         rF   _list_resource_templatesz&ProxyProvider._list_resource_templates  s     	++--Fv&,&D&D&F F +* "33D4G4GK*   v !,It~~7G H . F vvv  	ww||//	 	r,  c                  K   | j                   }||j                  | j                        s$| j                          d {    | j                   }|J |j                  D cg c]  }|j                  |      | }}|r+|D cg c]   }|j                  |j                        s|" }}|sy t        |t              S 7 c c}w c c}w wr.  )	r   r  r  rC  r  r0  r1  r2  r:   )rN   r   r1  r3  r(  r4  s         rF   _get_resource_templatez$ProxyProvider._get_resource_template  s      %%=t ?//111))E   ${{I{!aiin.HA{I#+J8awqyy/I8HJ8!122 2 JJs:   =CC  "C"C9C=C C&C*C
Cc                  K   	 | j                          d{   }|4 d{    |j                          d{   }|D cg c]"  }t        j                  | j                  |      $ }}ddd      d{    t        t        j                               | _        |S 7 7 7 jc c}w 7 4# 1 d{  7  sw Y   DxY w# t
        $ r+}|j                  j                  t        k(  rg }n Y d}~vd}~ww xY ww)z(List all prompts from the remote server.N)rQ   list_promptsr   r  r{   r   rE   r>   r   r  r  r  r!  )rN   rc   mcp_promptsppromptsr*  s         rF   _list_promptszProxyProvider._list_prompts  s     	++--Fv$*$7$7$99 )(  //0C0CQG(   v *'4>>3CD .9 vvv  	ww||// 	r,  c                  K   | j                   }||j                  | j                        s$| j                          d {    | j                   }|J |j                  D cg c]  }|j
                  |k(  s| }}|r+|D cg c]   }|j                  |j                        s|" }}|sy t        |t              S 7 ~c c}w c c}w wr.  )
r!  r  r  rK  r  r   r0  r1  r2  r:   )rN   r   r1  r3  rI  r4  s         rF   _get_promptzProxyProvider._get_prompt  s      ##=t ?$$&&&''E   ${{={!affnA{=#+J8awqyy/I8HJ8!122 ' >Jr6  c                   K   g S w)a5  Return empty list since proxy components don't support tasks.

        Override the base implementation to avoid calling list_tools() during
        server lifespan initialization, which would open the client before any
        context is set. All Proxy* components have task_config.mode="forbidden".
        rl   rN   s    rF   	get_taskszProxyProvider.get_tasks  s      	s   rL   )r{   r   r"  float | Noner   )rg   zSequence[Tool])r   rD   r1  VersionSpec | Nonerg   zTool | None)rg   zSequence[Resource])r   rD   r1  rR  rg   zResource | None)rg   zSequence[ResourceTemplate])r   rD   r1  rR  rg   zResourceTemplate | None)rg   zSequence[Prompt])r   rD   r1  rR  rg   zPrompt | None)rg   zSequence[FastMCPComponent])ri   rj   rk   r   rO   rQ   r+  r5  r<  r>  rC  rE  rK  rM  rP  r   r   s   @rF   r   r   0  s     J #'?&?  ?.$ 8<33"43	3&& 7;33!33	3&& 7;33!33	 3&& 8<33"43	3&rH   r   c                   t        | t              r| ddlm} ddlm} t        j                  ||z        rdj                  _        j                         r.t              t        u rt        j                  d       dfd}|S j                         rt        j                  d       dfd}|S dfd	}|S t        t        t        |             dfd
}|S )a^  Create a client factory from the given target.

    Internal helper that handles the session strategy based on the target type:
    - Connected Client: reuses existing session (with warning about context mixing)
    - Disconnected Client: creates fresh sessions per request
    - Other targets: creates ProxyClient and fresh sessions per request
    r   StreamableHttpTransportSSETransportTzqProxy detected connected ProxyClient - creating fresh sessions for each request to avoid request context leakage.c                 $     j                         S rL   newrc   s   rF   fresh_client_factoryz4_create_client_factory.<locals>.fresh_client_factory?  s    zz|#rH   zProxy detected connected client - reusing existing session for all requests. This may cause context mixing in concurrent scenarios.c                      S rL   rl   r[  s   rF   reuse_client_factoryz4_create_client_factory.<locals>.reuse_client_factoryJ  s    rH   c                 $     j                         S rL   rY  r[  s   rF   r\  z4_create_client_factory.<locals>.fresh_client_factoryO  s    ::<rH   c                 $     j                         S rL   rY  )base_clients   rF   proxy_client_factoryz4_create_client_factory.<locals>.proxy_client_factoryW  s    ??$$rH   r   )rR   r   fastmcp.client.transports.httprU  fastmcp.client.transports.sserW  	transportforward_incoming_headersis_connectedr   ProxyClientloggerinfor	   r   )targetrU  rW  r\  r^  rb  ra  rc   s         @@rF   _create_client_factoryrl    s    ( &&! 	K>f&&(?,(NO8<F5 T&\[%@KK<
$ (' KKI
 ('	  $# "$sF"34	% $#rH   c                  @     e Zd ZdZdd	 	 	 d fdZddZd	dZ xZS )
rf   a  A FastMCP server that acts as a proxy to a remote MCP-compliant server.

    This is a convenience wrapper that creates a FastMCP server with a
    ProxyProvider. For more control, use FastMCP with add_provider(ProxyProvider(...)).

    Example:
        ```python
        from fastmcp.server import create_proxy
        from fastmcp.server.providers.proxy import FastMCPProxy, ProxyClient

        # Create a proxy server using create_proxy (recommended)
        proxy = create_proxy("http://localhost:8000/mcp")

        # Or use FastMCPProxy directly with explicit client factory
        proxy = FastMCPProxy(client_factory=lambda: ProxyClient("http://localhost:8000/mcp"))
        ```
    warn)provider_error_strategyc                   t        |   di | || _        || _        t	        |      }| j                  |       | j                  j                  t        |              | j                          y)a  Initialize the proxy server.

        FastMCPProxy requires explicit session management via client_factory.
        Use create_proxy() for convenience with automatic session strategy.

        Args:
            client_factory: A callable that returns a Client instance when called.
                           This gives you full control over session creation and reuse.
                           Can be either a synchronous or asynchronous function.
            provider_error_strategy: How provider errors should affect aggregate
                operations. Defaults to ``"warn"`` for compatibility; use
                ``"raise"`` when the proxy should surface upstream failures.
            **kwargs: Additional settings for the FastMCP server.
        Nrl   )
rx   rO   ro  r{   r   add_provider
middlewarer   rJ   _setup_proxy_ping_handler)rN   r{   ro  r|   providerr}   s        rF   rO   zFastMCPProxy.__init__u  sa    * 	"6"'>$,*>:(#8>?&&(rH   c                   K   | j                         }t        j                  |      rt        t        | d {         }|S 7 
wrL   r$  r   s     rF   rQ   zFastMCPProxy._get_client  s:     $$&v&&,/F #/r   c                |     	 	 	 	 d fd}| j                   j                  t        j                  j                  <   y )Nc                R  K   j                          d {   }|4 d {    |j                          d {    d d d       d {    t        j                  j	                  t        j                  j                               S 7 r7 i7 S7 E# 1 d {  7  sw Y   UxY wwrL   )rQ   pingr@   rA   ServerResultEmptyResult)_requestrc   rN   s     rF   ping_remotez;FastMCPProxy._setup_proxy_ping_handler.<locals>.ping_remote  sq       ++--Fvkkm## v99))#))*?*?*ABB .# vvvsd   B'B

B'BB'BBBB'
BA B'B'BB'B$BB$ B')r{  zmcp.types.PingRequestrg   zmcp.types.ServerResult)_mcp_serverrequest_handlersr@   rA   PingRequest)rN   r|  s   ` rF   rs  z&FastMCPProxy._setup_proxy_ping_handler  s?    	C+	C#	C DO))#))*?*?@rH   )r{   r   ro  r0   r   rg   rh   )ri   rj   rk   r   rO   rQ   rs  r   r   s   @rF   rf   rf   b  s2    , :@	) ') "7	):	OrH   rf   c                R   K   t               }|j                          d{   S 7 w)zKForward list roots request from remote server to proxy's connected clients.N)r,   
list_roots)ra   rd   s     rF   default_proxy_roots_handlerr    s"      -C!!!!s   '%'c                b  K   t               }|j                  t        |       |j                  |j                  |j
                  |j                         d{   }t        j                  j                  d|j                  xs d      }t        j                  j                  dd|      S 7 Uw)	zIForward sampling request from remote server to proxy's connected clients.)system_prompttemperature
max_tokensmodel_preferencesNr    )r   r   	assistantzfastmcp-client)r  modelr   )r,   samplelistsystemPromptr  	maxTokensmodelPreferencesr@   rA   TextContentr   CreateMessageResult)r  r   ra   rd   r   r   s         rF   default_proxy_sampling_handlerr    s      -C::X))&&## 11   F ii##fkk6GR#HG99((	 )  s   AB/B-AB/c                  K   t               }t        |t              r|j                  ndi d}|j                  j                  | ||j                         d{   }t        |j                  |j                        S 7 %w)zLForward elicitation request from remote server to proxy's connected clients.object)r   
properties)r?   requestedSchemarelated_request_idN)actionr   )
r,   rR   r   r  sessionelicit
request_idr   r  r   )r?   response_typer   ra   rd   requested_schemar   s          rF   !default_proxy_elicitation_handlerr    s}      -C f56 	b1 
 ;;%%(>> &  F
 v}}fnnEEs   ABA?&Bc                   K   t               }| j                  j                  d      }| j                  j                  d      }|j                  || j                  | j
                  |       d{    y7 w)zIForward log notification from remote server to proxy's connected clients.msgextra)levellogger_namer  N)r,   datar   logr  ri  )r?   rd   r  r  s       rF   default_proxy_log_handlerr    sW     
-C
,,

5
!CLLW%E
''#W]]e'
TTTs   A-A7/A50A7c                Z   K   t               }|j                  | ||       d{    y7 w)zNForward progress notification from remote server to proxy's connected clients.N)r,   report_progress)progresstotalr?   rd   s       rF   default_proxy_progress_handlerr    s'      -C


hw
777s   !+)+c                F   ddl }ddlm}m} ddlm} | d   }|y|\  }}	 t        j                         }|j                  |j                  u rq|j                  |j                  k7  rWt        j                  |        |       }	|	8|j                   ||	             |j                  |j                  |	             yyyy# t        $ rX t        j                  |        |       }	|	7|j                   ||	             |j                  |j                  |	             Y yw xY w)u  Set the ``request_ctx``, ``_current_context`` and ``_current_server``
    ContextVars from stashed values.

    Called at the start of proxy handler invocations in
    ``StatefulProxyClient`` to fix stale ContextVars in the receive-loop
    task.  Only overrides when the ContextVar is genuinely stale (same
    session, different request_id) to avoid corrupting the concurrent
    case where multiple sessions share the same ref via ``copy.copy``.

    We stash a ``(RequestContext, weakref[FastMCP])`` tuple — never a
    ``Context`` instance — because ``Context`` properties are themselves
    ContextVar-dependent and would resolve stale values in the receive
    loop.  Instead we construct a fresh ``Context`` here after restoring
    ``request_ctx``, so its property accesses read the correct values.

    This is a set-only repair of a long-lived task's ContextVars, not a
    scope: we never ``reset()`` because the prior values are stale and
    the loop keeps running.  ``_current_server`` is restored alongside
    ``_current_context`` so handlers that resolve the server via
    dependency injection (e.g. ``get_server()``) see the right instance;
    it is set directly rather than via ``Context.__aenter__`` to avoid
    opening a context-manager lifecycle on an unscoped path.
    r   N)r+   _current_context)_current_server)weakreffastmcp.server.contextr+   r  fastmcp.server.dependenciesr  r   r   LookupErrorsetrefr  r  )
rc_refr  r+   r  r  stashedrcfastmcp_ref
current_rcfastmcps
             rF   _restore_request_contextr    s   4 @;QiGOB __&
 RZZ'J,A,AR]],R-  !12G 45  -S'  -  !12G 45s   B? ?AD D c                     d fd}|S )zWrap a proxy handler to restore request_ctx before delegating.

    The wrapper is a plain ``async def`` so it passes
    ``inspect.isfunction()`` checks in handler registration paths
    (e.g., ``create_roots_callback``).
    c                 F   K   t                | i | d {   S 7 wrL   )r  )argsr|   handlerr  s     rF   wrapperz(_make_restoring_handler.<locals>.wrapper-  s%      (d-f----s   !!)r  r   r|   r   rg   r   rl   )r  r  r  s   `` rF   _make_restoring_handlerr  %  s    . NrH   c                  (     e Zd ZdZ	 	 d fdZ xZS )rh  zA proxy client that forwards advanced interactions between a remote MCP server and the proxy's connected clients.

    Supports forwarding roots, sampling, elicitation, logging, and progress.
    c                F   d|vr| j                         |d<   d|vr	t        |d<   d|vr	t        |d<   d|vr	t        |d<   d|vr	t        |d<   d|vr	t
        |d<   t        |   dd|i| dd	lm	} dd
l
m} t        | j                  ||z        rd| j                  _        y y )Nr   rootssampling_handlerelicitation_handlerlog_handlerprogress_handlerre  r   rT  rV  Trl   )generate_namer  r  r  r  r  rx   rO   rc  rU  rd  rW  rR   re  rf  )rN   re  r|   rU  rW  r}   s        rF   rO   zProxyClient.__init__:  s     !//1F6N& 9F7OV+)GF%& .,MF()&$=F=!V+)GF%&7977
 	K>dnn&=&LM6:DNN3 NrH   )re  zcClientTransportT | FastMCP[Any] | FastMCP1Server | AnyUrl | Path | MCPConfig | dict[str, Any] | str)ri   rj   rk   r   rO   r   r   s   @rF   rh  rh  4  s    
!;!; !;rH   rh  c                  d     e Zd ZU dZded<   ded<   d fdZddZd fdZdd	Zd
 Z	ddZ
 xZS )rS   a  A proxy client that provides a stateful client factory for the proxy server.

    The stateful proxy client bound its copy to the server session.
    And it will be disconnected when the session is exited.

    This is useful to proxy a stateful mcp server such as the Playwright MCP server.
    Note that it is essential to ensure that the proxy server itself is also stateful.

    Because session reuse means the receive-loop task inherits a stale
    ``request_ctx`` ContextVar snapshot, the default proxy handlers are
    replaced with versions that restore the ContextVar before forwarding.
    ``ProxyTool.run`` stashes the current ``RequestContext`` in
    ``_proxy_rc_ref`` before each backend call, and the handlers consult
    it to detect (and correct) staleness.
    	list[Any]rW   zset[str]_proxy_restoring_handler_keysc                ,   d g| _         t               | _        dt        fdt        fdt
        fdt        fdt        ffD ]>  \  }}||vst        || j                         ||<   | j                  j                  |       @ t        | ,  |i | i | _        y )Nr  r  r  r  r  )rW   r  r  r  r  r  r  r  r  addrx   rO   _caches)rN   r  r|   r/  
default_fnr}   s        rF   rO   zStatefulProxyClient.__init__{  s     #V-0U*12!?@"$EF56!?@ 
OC & 5j$BTBTUs2266s; 
 	$)&)FHrH   c                P   d| j                   v r0t        t        t        | j                              | j
                  d<   d| j                   v r0t        t        t        | j                              | j
                  d<   d| j                   v r0t        t        t        | j                              | j
                  d<   d| j                   v r0t        t        t        | j                              | j
                  d<   d	| j                   v r t        t        | j                        | _        y y )
Nr  list_roots_callbackr  sampling_callbackr  elicitation_callbackr  logging_callbackr  )r  r   r  r  rW   _session_kwargsr   r  r   r  r   r  r  _progress_handlerrO  s    rF   _bind_restoring_handlersz,StatefulProxyClient._bind_restoring_handlers  s   d888:O'(CTEWEWX;D  !67 !C!CC8P'2D4F4F9D  !45
 !D$F$FF;V'5t7I7I<D  !78
 D>>>7J'(A4CUCUV8D  !34 !C!CC%<.0B0B&D" DrH   c                ~    t        t        t           t        |                }d g|_        |j                          |S rL   )r	   rS   r   rx   rZ  rW   r  )rN   
new_clientr}   s     rF   rZ  zStatefulProxyClient.new  s8    -.>?O
$(6
 ++-rH   c                   K   yw)zzThe stateful proxy client will be forced disconnected when the session is exited.

        So we do nothing here.
        Nrl   )rN   exc_type	exc_value	tracebacks       rF   	__aexit__zStatefulProxyClient.__aexit__  s     s   c                   K   | j                   rE| j                   j                         \  }}|j                  d       d{    | j                   rDyy7 w)z3Clear all cached clients and force disconnect them.TforceN)r  popitem_disconnect)rN   _r3  s      rF   clearzStatefulProxyClient.clear  sF     ll||++-HAu##$#/// ll/s   ?AAAAc                &    t               j                   j                  j                  d      [ j	                         t
        j                   d         j                  <    fd}j                  j                  |       S )zCreate a new stateful proxy client instance with the same configuration.

        Use this method as the client factory for stateful proxy server.
        Nz created for c                   K   j                   j                  d        t        j                    d       t	        j
                  d      5   j                  d       d {    d d d        y 7 # 1 sw Y   y xY ww)Nz will be disconnectT)shieldr  )r  popri  debugr]   CancelScoper  )proxy_clientrN   r  s   rF   _on_session_exitz:StatefulProxyClient.new_stateful.<locals>._on_session_exit  sj       $/~-@AB &&d3&222>>> 43> 43s0   AB A4$A2%A4)	B 2A44A=9B )	r,   r  r  r   rZ  ri  r  _exit_stackpush_async_callback)rN   r  r  r  s   ` @@rF   new_statefulz StatefulProxyClient.new_stateful  s    
 -''||''688:LLLL>wi@A$0DLL!	? 334DErH   )r  r   r|   r   r  )rg   z%StatefulProxyClient[ClientTransportT])rg   zClient[ClientTransportT])ri   rj   rk   r   r   rO   r  rZ  r  r  r  r   r   s   @rF   rS   rS   ^  s5    2 #++I&40rH   rS   )rE   	Exceptionrg   r   )rk  z}Client[ClientTransportT] | ClientTransport | FastMCP[Any] | FastMCP1Server | AnyUrl | Path | MCPConfig | dict[str, Any] | strrg   r   )ra   /RequestContext[ClientSession, LifespanContextT]rg   r   )r  zlist[mcp.types.SamplingMessage]r   z$mcp.types.CreateMessageRequestParamsra   r  rg   zmcp.types.CreateMessageResult)
r?   rD   r  r   r   zmcp.types.ElicitRequestParamsra   r  rg   r   )r?   r   rg   rh   )r  r  r  rQ  r?   rs   rg   rh   )r  r  rg   rh   )r  r   r  r  rg   r   )|r   
__future__r   r   r   r  collections.abcr   r   r   typingr   r   r	   r]   r[   	mcp.typesr@   r
   mcp.client.sessionr   mcp.server.lowlevel.serverr   mcp.shared.contextr   r   mcp.shared.exceptionsr   r   r   r   r   pydantic.networksr   fastmcp.client.clientr   r   fastmcp.client.elicitationr   r   fastmcp.client.loggingr   r   fastmcp.client.rootsr   r   fastmcp.client.samplingr   fastmcp.client.telemetryr   fastmcp.client.transportsr   fastmcp.exceptionsr    fastmcp.mcp_configr!   fastmcp.promptsr"   r#   r$   fastmcp.prompts.baser%   fastmcp.resourcesr&   r'   fastmcp.resources.baser(   r)   fastmcp.resources.templater*   r  r+   r  r,   fastmcp.server.middlewarer-   r.   r/   "fastmcp.server.providers.aggregater0   fastmcp.server.providers.baser1   fastmcp.server.serverr2   fastmcp.server.tasks.configr3   fastmcp.tools.baser4   r5   fastmcp.utilities.componentsr6   r7   fastmcp.utilities.loggingr8   fastmcp.utilities.versionsr9   r:   pathlibr;   r<   ri   ri  r   rG   rJ   rn   r   r   r   r  r  r   r   rl  rf   r  r  r  r  r  r  r  rh  rS   rl   rH   rF   <module>r     s\   #    9 9 + +     , 2 ? *  % 8 P B A < 0 6 , ( 9 9 / 8 B : * 3 M M D 2 ) 2 / O 0 D9	H	 "f*%Yv5F1F(GG#(
 #(Vn
 n
bh
H h
Vp
$ p
fP
& P
p
9 
9 " E !_H _T@$	@$ @$P?O7 ?ON"<""-0 = #	.FFF *F =	F
 F,U888 8 
	82626	26j';&)* ';Tv+&67 vrH   