
    {h+                        d 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 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mZ ddlmZ ddlmZmZmZ ddlm Z   ejB                  e"      Z# G d d      Z$y)z/StreamableHTTP Session Manager for MCP servers.    )annotationsN)AsyncIterator)
HTTPStatus)Any)uuid4)
TaskStatus)Request)Response)ReceiveScopeSend)Server)MCP_SESSION_ID_HEADER
EventStoreStreamableHTTPServerTransport)TransportSecuritySettingsc                      e Zd ZdZ	 	 	 	 d	 	 	 	 	 	 	 	 	 d	dZej                  d
d       Z	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 ddZ		 	 	 	 	 	 	 	 ddZ
y)StreamableHTTPSessionManagera  
    Manages StreamableHTTP sessions with optional resumability via event store.

    This class abstracts away the complexity of session management, event storage,
    and request handling for StreamableHTTP transports. It handles:

    1. Session tracking for clients
    2. Resumability via an optional event store
    3. Connection management and lifecycle
    4. Request handling and transport setup

    Important: Only one StreamableHTTPSessionManager instance should be created
    per application. The instance cannot be reused after its run() context has
    completed. If you need to restart the manager, create a new instance.

    Args:
        app: The MCP server instance
        event_store: Optional event store for resumability support.
                     If provided, enables resumable connections where clients
                     can reconnect and receive missed events.
                     If None, sessions are still tracked but not resumable.
        json_response: Whether to use JSON responses instead of SSE streams
        stateless: If True, creates a completely fresh transport for each request
                   with no session tracking or state persistence between requests.
    Nc                    || _         || _        || _        || _        || _        t        j                         | _        i | _        d | _	        t        j                         | _
        d| _        y )NF)appevent_storejson_response	statelesssecurity_settingsanyioLock_session_creation_lock_server_instances_task_group	_run_lock_has_started)selfr   r   r   r   r   s         c/var/www/html/hubwallet-dev/venv/lib/python3.12/site-packages/mcp/server/streamable_http_manager.py__init__z%StreamableHTTPSessionManager.__init__8   sa     &*"!2 ',jjl#KM  !    c                 K   | j                   4 d{    | j                  rt        d      d| _        ddd      d{    t        j                         4 d{   }|| _        t        j                  d       	 d t        j                  d       |j                  j                          d| _        | j                  j                          ddd      d{    y7 7 # 1 d{  7  sw Y   xY w7 # t        j                  d       |j                  j                          d| _        | j                  j                          w xY w7 u# 1 d{  7  sw Y   yxY ww)aw  
        Run the session manager with proper lifecycle management.

        This creates and manages the task group for all session operations.

        Important: This method can only be called once per instance. The same
        StreamableHTTPSessionManager instance cannot be reused after this
        context manager exits. Create a new instance if you need to restart.

        Use this in the lifespan context manager of your Starlette app:

        @contextlib.asynccontextmanager
        async def lifespan(app: Starlette) -> AsyncIterator[None]:
            async with session_manager.run():
                yield
        NzyStreamableHTTPSessionManager .run() can only be called once per instance. Create a new instance if you need to run again.Tz&StreamableHTTP session manager startedz,StreamableHTTP session manager shutting down)r    r!   RuntimeErrorr   create_task_groupr   loggerinfocancel_scopecancelr   clear)r"   tgs     r#   runz StreamableHTTPSessionManager.runP   s7    & >> 	% 	%  "Y  !%D	% 	% **, 	/ 	/!DKK@A/JK&&(#' &&,,.	/ 	/ 	/	% 	% 	% 	% 	%	/ JK&&(#' &&,,.	/ 	/ 	/ 	/s   E-C&E-C*E-C(E-C?E-"E DAEE- E!E-(E-*C<0C31C<8E-AEEE-E*E!E*&E-c                   K   | j                   t        d      | j                  r| j                  |||       d{    y| j	                  |||       d{    y7 !7 w)a  
        Process ASGI request with proper session handling and transport setup.

        Dispatches to the appropriate handler based on stateless mode.

        Args:
            scope: ASGI scope
            receive: ASGI receive function
            send: ASGI send function
        Nz6Task group is not initialized. Make sure to use run().)r   r'   r   _handle_stateless_request_handle_stateful_request)r"   scopereceivesends       r#   handle_requestz+StreamableHTTPSessionManager.handle_requesty   sc       #WXX >>00FFF//wEEE GEs!   :A"AA"A A" A"c                   K   t         j                  d       t        d j                  d j                        t
        j                  dd fd} j                  J  j                  j                  |       d{    j                  |||       d{    j                          d{    y7 87 7 	w)z
        Process request in stateless mode - creating a new transport for each request.

        Args:
            scope: ASGI scope
            receive: ASGI receive function
            send: ASGI send function
        z7Stateless mode: Creating new transport for this requestNmcp_session_idis_json_response_enabledr   r   task_statusc                  K   j                         4 d {   }|\  }}| j                          	 j                  j                  ||j                  j	                         d       d {    d d d       d {    y 7 j7 # t
        $ r t        j                  d       Y 5w xY w7 -# 1 d {  7  sw Y   y xY ww)NTr   zStateless session crashed)connectstartedr   r/   create_initialization_options	Exceptionr)   	exception)r<   streamsread_streamwrite_streamhttp_transportr"   s       r#   run_stateless_serverzTStreamableHTTPSessionManager._handle_stateless_request.<locals>.run_stateless_server   s     %--/ B B7,3)\##%B((,,#$>>@"&	 '   	B B B ! B$$%@ABB B B Bss   CBCB-:B-B.B2C=B+>CBB(%B-'B((B-+C-B?3B64B?;C)r<   TaskStatus[None])r)   debugr   r   r   r   TASK_STATUS_IGNOREDr   startr6   	terminate)r"   r3   r4   r5   rH   rG   s   `    @r#   r1   z6StreamableHTTPSessionManager._handle_stateless_request   s      	NO6%)%7%7"44	
 KPJcJc 	B 	B +++$$%9::: ++E7DAAA &&((( 	; 	B 	)s6   A=C B:C B<C 4B>5C <C >C c                   
K   t        ||      }|j                  j                  t              }|N| j                  v r@ j                  |   }t
        j                  d       |j                  |||       d{    y|t
        j                  d        j                  4 d{    t               j                  }t        | j                   j                   j                        

j                  J 
 j                  
j                  <   t
        j!                  d|        t"        j$                  dd

 fd} j&                  J  j&                  j)                  |       d{    
j                  |||       d{    ddd      d{    yt+        dt,        j.                  	      }	 |	|||       d{    y7 V7 +7 d7 K7 =# 1 d{  7  sw Y   yxY w7 &w)z
        Process request in stateful mode - maintaining session state between requests.

        Args:
            scope: ASGI scope
            receive: ASGI receive function
            send: ASGI send function
        Nz1Session already exists, handling request directlyzCreating new transportr8   z'Created new transport with session ID: r;   c                t  K   j                         4 d {   }|\  }}| j                          	 j                  j                  ||j                  j	                         d       d {    j                  r_j                  j                  v rGj                  s;t        j                  dj                   d       j                  j                  = 	 d d d       d {    y 7 7 # t
        $ r1}t        j                  dj                   d| d       Y d }~d }~ww xY w# j                  raj                  j                  v rHj                  s;t        j                  dj                   d       j                  j                  = w w w w xY w7 # 1 d {  7  sw Y   y xY ww)	NFr>   zSession z
 crashed: T)exc_infozCleaning up crashed session z from active instances.)r?   r@   r   r/   rA   rB   r)   errorr9   r   is_terminatedr*   )r<   rD   rE   rF   erG   r"   s        r#   
run_serverzIStreamableHTTPSessionManager._handle_stateful_request.<locals>.run_server   s    -557 Z Z74;1\#++-Z"&((,, + , $ F F H*/	 #/ #   !/ = =$2$A$ATE[E[$[(6(D(D &$B'5'D'D&E F8%8!"
 %)$:$:>;X;X$Y7Z Z Z  ) "LL"*>+H+H*ITUSV W)- )   !/ = =$2$A$ATE[E[$[(6(D(D &$B'5'D'D&E F8%8!"
 %)$:$:>;X;X$Y )E %\ !>%Z Z Z Zs   F8C.F8F#:C2-C0.C22A+F#F8(F!)F80C22	D,;'D'"D/'D,,D//A/FF#!F8#F5)F,*F51F8z)Bad Request: No valid session ID provided)status_code)r<   rI   returnNone)r	   headersgetr   r   r)   rJ   r6   r   r   hexr   r   r   r   r9   r*   r   rK   r   rL   r
   r   BAD_REQUEST)r"   r3   r4   r5   requestrequest_mcp_session_id	transportnew_session_idrT   responserG   s   `         @r#   r2   z5StreamableHTTPSessionManager._handle_stateful_request   s     %)!(!4!45J!K "-2HDLbLb2b../EFILLLM**5'4@@@!)LL1222 2J 2J!&!>#1-1-?-? $ 0 0&*&<&<	" &44@@@HV&&~'D'DEEnEUVW INHaHa Z Z> ''333&&,,Z888 %33E7DIIIe2J 2J 2Jj  ;&22H 5'4000 A2J^ 9 Je2J 2J 2J 2Jr 1s   A6G3:G;-G3(G)G3,CG1G2GGGG3G-G3
G1G3G3GGG3G."G%#G.*G3)NFFN)
r   zMCPServer[Any, Any]r   zEventStore | Noner   boolr   ra   r   z TransportSecuritySettings | None)rV   zAsyncIterator[None])r3   r   r4   r   r5   r   rV   rW   )__name__
__module____qualname____doc__r$   
contextlibasynccontextmanagerr/   r6   r1   r2    r%   r#   r   r      s    : *.#>B" " '" 	"
 " <"0 ##&/ $&/PFF F 	F
 
F2/)/) /) 	/)
 
/)bT1T1 T1 	T1
 
T1r%   r   )%re   
__future__r   rf   loggingcollections.abcr   httpr   typingr   uuidr   r   	anyio.abcr   starlette.requestsr	   starlette.responsesr
   starlette.typesr   r   r   mcp.server.lowlevel.serverr   	MCPServermcp.server.streamable_httpr   r   r   mcp.server.transport_securityr   	getLoggerrb   r)   r   rh   r%   r#   <module>rx      s`    5 "   )       & ( 0 0 : 
 D			8	$z1 z1r%   