
    Cj>                        S SK Jr  S SKrS SKJrJrJr  S SKJr  S SK	J
r
  S SKJr  SSKJr  S	S
KJr  \
 " S S5      5       r " S S5      r      SS jr " S S\S   5      rg)    )annotationsN)	AwaitableCallableIterable)AbstractAsyncContextManager)	dataclass)Any   )logger   )	MCPServerc                  4    \ rS rSr% S\S'   S\S'   S\S'   Srg	)
_ServerCommand   stractionfloat | Nonetimeout_secondszasyncio.Future[None]future N)__name__
__module____qualname____firstlineno____annotations____static_attributes__r       d/home/rurouni/.local/share/pipx/venvs/strix-agent/lib/python3.13/site-packages/agents/mcp/manager.pyr   r      s    K!!  r   r   c                  j    \ rS rSr        S
S jr\SS j5       rSS jrSS jrSS jr	SS jr
Srg	)_ServerWorker   c                    Xl         X l        X0l        [        R                  " 5       U l        [        R                  " U R                  5       5      U l        g N)	_server_connect_timeout_seconds_cleanup_timeout_secondsasyncioQueue_queuecreate_task_run_task)selfserverconnect_timeout_secondscleanup_timeout_secondss       r   __init___ServerWorker.__init__   s:     (?%(?%5<]]_((5
r   c                6    U R                   R                  5       $ r#   )r,   doner-   s    r   is_done_ServerWorker.is_done!   s    zz  r   c                X   #    U R                  SU R                  5      I S h  vN   g  N7f)Nconnect)_submitr%   r5   s    r   r9   _ServerWorker.connect%        ll9d&C&CDDD    *(*c                X   #    U R                  SU R                  5      I S h  vN   g  N7f)Ncleanup)r:   r&   r5   s    r   r?   _ServerWorker.cleanup(   r<   r=   c                   #    [         R                  " 5       nUR                  5       nU R                  R	                  [        XUS95      I S h  vN   UI S h  vN   g  N N7f)N)r   r   r   )r'   get_running_loopcreate_futurer)   putr   )r-   r   r   loopr   s        r   r:   _ServerWorker._submit+   sY     '')'+'9'9';kkoo&RXY
 	
 	
 	
 	s$   AA"A	A"A A" A"c                  #     U R                   R                  5       I S h  vN nUR                  S:H  n UR                  S:X  a3  [        U R                  R
                  UR                  5      I S h  vN   O[UR                  S:X  a3  [        U R                  R                  UR                  5      I S h  vN   O[        SUR                   35      eUR                  R                  5       (       d  UR                  R                  S 5        U(       a  g GM   N N Nf! [         aD  nUR                  R                  5       (       d  UR                  R                  U5         S nANZS nAff = f7f)Nr?   r9   zUnknown command: )r)   getr   _run_with_timeout_in_taskr$   r9   r   r?   
ValueErrorr   	cancelled
set_resultBaseExceptionset_exception)r-   commandshould_exitexcs       r   r+   _ServerWorker._run3   s     KKOO--G!..I5K6>>Y.3DLL4H4H'JaJabbb^^y03DLL4H4H'JaJabbb$'88H%IJJ~~//11NN--d3 ! - cb
 ! 6~~//11NN0056s^   E0DE0=D 3D4AD 6D7AD E0D D 
E-):E(#E0(E--E0)r&   r%   r)   r$   r,   N)r.   r   r/   r   r0   r   returnNone)rS   boolrS   rT   )r   r   r   r   rS   rT   )r   r   r   r   r1   propertyr6   r9   r?   r:   r+   r   r   r   r   r    r       sW    
6
6 ".
6 ".	
6
 

6 ! !EEr   r    c                  ^^#    Uc  U " 5       I S h  vN   g [        [        SS 5      nUb1  U" U5       IS h  vN   U " 5       I S h  vN   S S S 5      IS h  vN   g [        R                  " 5       mTc#  [        R                  " U " 5       US9I S h  vN   g Sm[        R                  " 5       nSUU4S jjnUR                  X5      n U " 5       I S h  vN    UR                  5         g  N N N N! , IS h  vN  (       d  f       g = f Nz N:! [        R                   a#  nT(       a  [        R                  " 5       Uee S nAff = f! UR                  5         f = f7f)Ntimeout)rY   Fc                 *   > SmT R                  5         g )NT)cancel)task	timed_outs   r   _cancel*_run_with_timeout_in_task.<locals>._cancel\   s    	r   rV   )	getattrr'   current_taskwait_forrB   
call_laterCancelledErrorTimeoutErrorr[   )	funcr   timeout_contextrE   r^   handlerQ   r\   r]   s	          @@r   rI   rI   G   s      fgy$7O""?33&LL 43!D|tv???I##%D 
 ___6Ff 	7 	 4 433 	@ 	!! &&(c1
 	s   E C+%E C-E C3C/C3E C1:E D6E 

D DD E -E /C31E 3D
9C<:D
E D E%EEE EE c                  T   \ rS rSrSrSSSSSSS.               SS jjr\S S j5       r\S S	 j5       rS!S
 jr	S"S jr
S S jrSS.S#S jjrS$S jr      S%S jrSS.     S&S jjrS$S jrS'S jrS(S jrS(S jrS)S jrS*S jrS+S jrS(S jrS,S jr\S,S j5       rSrg)-MCPServerManagerl   a  Manage MCP server lifecycles and expose only connected servers.

Use this helper to keep MCP connect/cleanup on the same task and avoid
run failures when a server is unavailable. The manager will attempt to
connect each server and then expose the connected subset via
`active_servers`.

Basic usage:
    async with MCPServerManager([server_a, server_b]) as manager:
        agent = Agent(
            name="Assistant",
            instructions="...",
            mcp_servers=manager.active_servers,
        )

FastAPI lifespan example:
    @asynccontextmanager
    async def lifespan(app: FastAPI):
        async with MCPServerManager([server_a, server_b]) as manager:
            app.state.mcp_manager = manager
            yield

    app = FastAPI(lifespan=lifespan)

Important behaviors:
- `active_servers` only includes servers that connected successfully.
  `failed_servers` holds the failures and `errors` maps servers to errors.
- `drop_failed_servers=True` removes failed servers from `active_servers`
  (recommended). If False, `active_servers` will still include all servers.
- `strict=True` raises on the first connection failure. If False, failures
  are recorded and the run can proceed with the remaining servers.
- `reconnect(failed_only=True)` retries failed servers and refreshes
  `active_servers`.
- `connect_in_parallel=True` uses a dedicated worker task per server to
  allow concurrent connects while preserving task affinity for cleanup.
g      $@TF)r/   r0   drop_failed_serversstrictsuppress_cancelled_errorconnect_in_parallelc                   [        U5      U l        [        U5      U l        X l        X0l        X@l        XPl        X`l        Xpl        0 U l	        / U l
        [        5       U l        [        5       U l        0 U l        g r#   )list_all_servers_active_serversr/   r0   rl   rm   rn   ro   _workersfailed_serversset_failed_server_set_connected_serverserrors)r-   serversr/   r0   rl   rm   rn   ro   s           r   r1   MCPServerManager.__init__   si     !M#G}'>$'>$#6 (@%#6 8:/125%25%68r   c                ,    [        U R                  5      $ )z8Return the active MCP servers after connection attempts.)rq   rs   r5   s    r   active_serversMCPServerManager.active_servers   s     D(())r   c                ,    [        U R                  5      $ )z0Return all MCP servers managed by this instance.)rq   rr   r5   s    r   all_serversMCPServerManager.all_servers   s     D%%&&r   c                B   #    U R                  5       I S h  vN   U $  N7fr#   )connect_allr5   s    r   
__aenter__MCPServerManager.__aenter__   s!         	!s   c                @   #    U R                  5       I S h  vN   g  N7fr#   )cleanup_all)r-   exc_typeexc_valexc_tbs       r   	__aexit__MCPServerManager.__aexit__   s         	!s   c                j  #    [        U R                  5      n[        U R                  5      n/ U l        [        5       U l        0 U l        U R                  U R                  5      n/ n U R                  (       a  U R                  U5      I Sh  vN   OEU H>  nU R                  U5      I Sh  vN   XPR
                  ;  d  M-  UR                  U5        M@      U R#                  5         U R                  $  Nf NI! [         a    U R                  (       a  U R                  U5      I Sh  vN    O9U R                  / UQU R                  Q5      nU R                  U5      I Sh  vN    U R                   (       a0  U R                   Vs/ s H  oUU;   d  M
  UPM     Os  snf snU l        e X l        e f = f7f)z8Connect all servers in order and return the active list.N)rv   rx   rq   rs   ru   rw   ry   _servers_to_connectrr   ro   _connect_all_parallel_attempt_connectappendrM   _cleanup_servers_unique_serversrl   _refresh_active_servers)r-   previous_connected_serversprevious_active_serversservers_to_connectconnected_serversr.   servers_to_cleanups          r   r   MCPServerManager.connect_all   s    %()@)@%A""&t';';"< "%%!55d6G6GH-/	''001CDDD0F//777%<%<<)008 1( 	$$&###1 E 8  	''++,>???%)%9%9>'>$*=*=>&" ++,>???'')-):):():vHb>bF):($
  (?$	s~   A%F3(%C8 C4C8 F3C8 ,C6-C8 C8 F34C8 6C8 8/F0'D*(9F0!E$"$F0	FFF00F3)failed_onlyc                 #    U(       a  U R                  U R                  5      nOJU R                  5       I Sh  vN   [        U R                  5      n/ U l        [        5       U l        0 U l        U R                  U5      n U R                  (       a  U R                  U5      I Sh  vN   O"U H  nU R                  U5      I Sh  vN   M     U R                  5         U R                  $  N NE N(! U R                  5         f = f7f)zReconnect servers and return the active list.

Args:
    failed_only: If True, only retry servers that previously failed.
        If False, cleanup and retry all servers.
N)r   ru   r   rq   rr   rv   rw   ry   r   ro   r   r   r   rs   )r-   r   servers_to_retryr.   s       r   	reconnectMCPServerManager.reconnect   s      #33D4G4GH""$$$#D$5$56"$D&)eD#DK334DE	+''001ABBB.F//777 / ((*### % C 8((*sL   7DC+AD%C1 'C-(C1 C/C1 D-C1 /C1 1DDc                  #    [        U R                  5       H  n U R                  U5      I Sh  vN   M     g N	! [        R                   aQ  nU R
                  (       d  e [        R                  " SUR                   SU 35        X R                  U'    SnAM  SnAf[         a?  n[        R                  " SUR                   SU 35        X R                  U'    SnAM  SnAff = f7f)z%Cleanup all servers in reverse order.N"Cleanup cancelled for MCP server '': Failed to cleanup MCP server ')reversedrr   _cleanup_serverr'   rd   rn   r   debugnamery   	Exception	exception)r-   r.   rQ   s      r   r   MCPServerManager.cleanup_all   s     t001F	***6222 22)) *44A&++cRUQVWX&)F# *  #A&++cRUQV!WX&)F#*sJ   C+<:<C+<C(ABC+C()4C#C+#C((C+c                6   #    [        X5      I S h  vN   g  N7fr#   )rI   )r-   rf   r   s      r   _run_with_timeout"MCPServerManager._run_with_timeout  s      (>>>s   Nraise_on_errorc               (  #    Uc  U R                   n U R                  U5      I S h  vN   U R                  R                  U5        XR                  ;   a.  U R                  U5        U R                  R                  US 5        g g  N]! [        R                   a,  nU R                  (       d  e U R                  XSS9   S nAg S nAf[         a"  nU R                  XSS9  U(       a  e  S nAg S nAf[         a  nU R                  XSS9  e S nAff = f7f)Nr9   )phase)rm   _run_connectrx   addru   _remove_failed_serverry   popr'   rd   rn   _record_failurer   rM   )r-   r.   r   rQ   s       r   r   !MCPServerManager._attempt_connect  s      !![[N	##F+++##''/,,,**62- - ,
 %% 	?00  I > 	  I >  	  I >	s]   DB BAB DB D"C=DDC,'D,D9D

DDc                    U R                   (       a?  [        U R                  5      nU R                   Vs/ s H  o"U;  d  M
  UPM     snU l        g [        U R                  5      U l        g s  snf r#   )rl   rv   rw   rr   rs   rq   )r-   failedr.   s      r   r   (MCPServerManager._refresh_active_servers)  sY    ##001F9=9J9J#c9Jv\bNbF9J#cD #'(9(9#:D  $ds   	A,A,c                    [         R                  " SU SUR                   SU 35        XR                  ;  a6  U R                  R                  U5        U R                  R                  U5        X R                  U'   g )Nz
Failed to z MCP server 'r   )r   r   r   rw   ru   r   r   ry   )r-   r.   rQ   r   s       r   r    MCPServerManager._record_failure0  sg    :eWM&++c#OP000&&v.##''/!Fr   c                   #    U R                   (       a*  U R                  U5      nUR                  5       I S h  vN   g U R                  UR                  U R                  5      I S h  vN   g  N4 N7fr#   )ro   _get_workerr9   r   r/   r-   r.   workers      r   r   MCPServerManager._run_connect7  sV     ##%%f-F.."""((9U9UVVV #Vs!   6A1A-.A1'A/(A1/A1c                  #    U R                   (       a  XR                  ;   a  U R                  U   nUR                  (       a8  U R                  R                  US 5        U R                  R                  U5        g  UR                  5       I S h  vN   U R                  R                  US 5        U R                  R                  U5        g  U R                  UR                  U R                  5      I S h  vN   U R                  R                  U5        g  N! U R                  R                  US 5        U R                  R                  U5        f = f N^! U R                  R                  U5        f = f7fr#   )	ro   rt   r6   r   rx   discardr?   r   r0   r   s      r   r    MCPServerManager._cleanup_server>  s    ##--(?]]6*F~~!!&$/''//78nn&&&!!&$/''//7	4((9U9UVVV##++F3 '!!&$/''//7 W##++F3sZ   A9E7<D DD 8E7)E 6E7E ;E7D 9EE7E E44E7c                  #    [        [        U5      5       H  n U R                  U5      I S h  vN   M     g  N	! [        R                   aQ  nU R
                  (       d  e [        R                  " SUR                   SU 35        X0R                  U'    S nAM  S nAf[         a?  n[        R                  " SUR                   SU 35        X0R                  U'    S nAM  S nAff = f7f)Nr   r   r   )r   rq   r   r'   rd   rn   r   r   r   ry   r   r   )r-   rz   r.   rQ   s       r   r   !MCPServerManager._cleanup_serversP  s     tG}-F	***6222 .2)) *44A&++cRUQVWX&)F# *  #A&++cRUQV!WX&)F#*sJ   C*;9;C*;C'ABC*C'(4C"C*"C''C*c           
     f  #    U Vs/ s H'  n[         R                  " U R                  USS95      PM)     nn[         R                  " USS06I S h  vN nU R                  (       d*  U H$  n[        U[         R                  5      (       d  M$  Ue   U H;  n[        U[        5      (       d  M  [        U[         R                  5      (       a  M;  Ue   U R                  (       a  U R                  (       a  S nU R                  (       aU  U R                   HD  nU R                  R                  U5      nUb  [        U[         R                  5      (       a  MB  Un  O   OU R                  S   nUb9  U R                  R                  U5      nUb  Ue[        SUR                   S35      eg g g s  snf  GN^7f)NFr   return_exceptionsTr   zFailed to connect MCP server '')r'   r*   r   gatherrn   
isinstancerd   rM   rm   ru   ry   rH   RuntimeErrorr   )r-   rz   r.   tasksresultsresultfirst_failureerrors           r   r   &MCPServerManager._connect_all_parallel]  s     "
!  5 5fU 5 ST! 	 
  FFF,,!fg&<&<== L " F&-00FGLbLb9c9c  ;;4.. M,,"11F KKOOF3E}
5':P:P(Q(Q $*M 2 !% 3 3A 6(6$K"%CMDVDVCWWX#YZZ	 ) /;
 Gs.   F1.F)F1F.7F1F1,F1C"F1c                    U R                   R                  U5      nUb  UR                  (       a-  [        UU R                  U R
                  S9nX R                   U'   U$ )N)r.   r/   r0   )rt   rH   r6   r    r/   r0   r   s      r   r   MCPServerManager._get_worker{  sS    ""6*>V^^"(,(D(D(,(D(DF
 %+MM&!r   c                    XR                   ;   a  U R                   R                  U5        U R                   Vs/ s H  o"U:w  d  M
  UPM     snU l        g s  snf r#   )rw   removeru   )r-   r.   failed_servers      r   r   &MCPServerManager._remove_failed_server  sP    ,,,##**62/3/B/B
/BmW]F]M/B
 
s   	AAc                    U R                  U5      nU R                  (       d  U$ U Vs/ s H  o3U R                  ;  d  M  UPM     sn$ s  snf r#   )r   rx   )r-   rz   uniquer.   s       r   r   $MCPServerManager._servers_to_connect  sC    %%g.&&M%+UV6T=T=T/TVUUUs   A	 A	c                    [        5       n/ nU  H,  nX1;  d  M
  UR                  U5        UR                  U5        M.     U$ r#   )rv   r   r   )rz   seenr   r.   s       r   r    MCPServerManager._unique_servers  s=    "u"$F! f%  r   )rs   rr   rx   rw   rt   r0   ro   r/   rl   ry   ru   rm   rn   )rz   Iterable[MCPServer]r/   r   r0   r   rl   rU   rm   rU   rn   rU   ro   rU   rS   rT   )rS   list[MCPServer])rS   rj   )rS   bool | None)r   rU   rS   r   rV   rf   zCallable[[], Awaitable[Any]]r   r   rS   rT   )r.   r   r   r   rS   rT   )r.   r   rQ   rM   r   r   rS   rT   )r.   r   rS   rT   )rz   r   rS   rT   )rz   r   rS   rT   )r.   r   rS   r    )rz   r   rS   r   )r   r   r   r   __doc__r1   rW   r}   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   staticmethodr   r   r   r   r   rj   rj   l   s;   #R 1504$()-$)9$9 ".	9
 ".9 "9 9 #'9 "9 
94 * * ' '$$L 6: $6*?0?CO?	? CG4?	.;"W4$*[<	
V  r   rj   r   )
__future__r   r'   collections.abcr   r   r   
contextlibr   dataclassesr   typingr	   r   r.   r   r   r    rI   rj   r   r   r   <module>r      sw    "  9 9 2 !    ! ! !0 0f"
&"9E"	"Jo23EF or   