
    FJj<                       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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mZmZ ddlmZ ddlmZmZm Z  ddl!m"Z" ddl#m$Z$m%Z%m&Z&  ejN                  e(      Z) G d d      Z*y)z/StreamableHTTP Session Manager for MCP servers.    )annotationsN)AsyncIterator)Any)uuid4)
TaskStatus)Request)Response)ReceiveScopeSend)AuthenticatedUserAuthorizationContextauthorization_context)Server)MCP_SESSION_ID_HEADER
EventStoreStreamableHTTPServerTransport)TransportSecuritySettings)INVALID_REQUEST	ErrorDataJSONRPCErrorc                      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
    5. Idle session cleanup via optional timeout

    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.
        security_settings: Optional transport security settings.
        retry_interval: Retry interval in milliseconds to suggest to clients in SSE retry field. Used for SSE
            polling behavior.
        session_idle_timeout: Optional idle timeout in seconds for stateful sessions. If set, sessions that
            receive no HTTP requests for this duration will be automatically terminated and removed. When
            retry_interval is also configured, ensure the idle timeout comfortably exceeds the retry interval to
            avoid reaping sessions during normal SSE polling gaps. Default is None (no timeout). A value of 1800
            (30 minutes) is recommended for most deployments.
    Nc                D   ||dk  rt        d      |r|t        d      || _        || _        || _        || _        || _        || _        || _        t        j                         | _        i | _        i | _        d | _        t        j                         | _        d| _        y )Nr   z9session_idle_timeout must be a positive number of secondsz7session_idle_timeout is not supported in stateless modeF)
ValueErrorRuntimeErrorappevent_storejson_response	statelesssecurity_settingsretry_intervalsession_idle_timeoutanyioLock_session_creation_lock_server_instances_session_owners_task_group	_run_lock_has_started)selfr   r   r   r    r!   r"   r#   s           l/Users/ahmed/devFolder/claude-voice/.venv/lib/python3.12/site-packages/mcp/server/streamable_http_manager.py__init__z%StreamableHTTPSessionManager.__init__A   s      +0D0IXYY-9XYY&*"!2,$8! ',jjl#KM AC  !    c               F  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                          | 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                          | j                  j                          w xY w7 # 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+   r   r$   create_task_groupr)   loggerinfocancel_scopecancelr'   clearr(   )r,   tgs     r-   runz StreamableHTTPSessionManager.rune   s3    & >>>  "Y  !%D "> **,,!DKK@A	-JK&&(#' &&,,.$$**, -,, ">>>> - JK&&(#' &&,,.$$**, -,,,s   F!D F!DF!DF!DF!"F DA*F/F!:F
;F!F!D
DDF!A,FF
F!FFFF!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_request   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   r8   create_initialization_options	Exceptionr2   	exception)rD   streamsread_streamwrite_streamhttp_transportr,   s       r-   run_stateless_serverzTStreamableHTTPSessionManager._handle_stateless_request.<locals>.run_stateless_server   s     %--//7,3)\##%B((,,#$>>@"&	 '   	 0// ! B$$%@AB 0///ss   CBCB-:B-B.B2C=B+>CBB(%B-'B((B-+C-B?3B64B?;C)rD   TaskStatus[None])r2   debugr   r   r!   r$   TASK_STATUS_IGNOREDr)   startr?   	terminate)r,   r<   r=   r>   rP   rO   s   `    @r-   r:   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                z   K   t        ||      }|j                  j                  t              }|j                  d      }t	        |t
              rt        |      nd}|| j                  v r j                  |   }| j                  j                  |      k7  rht        j                  d|dd        t        ddt        t        d      	      }	t        |	j                  d
d
      dd      }
 |
|||       d{    yt        j!                  d       |j"                  < j$                  0t'        j(                          j$                  z   |j"                  _        |j-                  |||       d{    y|Et        j!                  d        j.                  4 d{    t1               j2                  }t5        | j6                   j8                   j:                   j<                        j>                  J || j                  j>                  <    j                  j>                  <   t        jA                  d|        t&        jB                  dd fd} jD                  J  jD                  jG                  |       d{    j-                  |||       d{    ddd      d{    yt        ddt        t        d      	      }	t        |	j                  d
d
      dd      }
 |
|||       d{    y7 7 7 u7 7 o7 a# 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
        userNz\Rejecting request for session %s: credential does not match the one that created the session@   z2.0zserver-errorzSession not found)codemessage)jsonrpciderrorT)by_aliasexclude_nonei  zapplication/json)status_code
media_typez1Session already exists, handling request directlyzCreating new transport)rA   rB   r   r!   r"   z'Created new transport with session ID: rC   c                
  K   j                         4 d {   }|\  }}| j                          	 t        j                         }j                  -t        j
                         j                  z   |_        |_        |5  j                  j                  ||j                  j                         d       d {    d d d        |j                  rj                  J t        j                  dj                   d       j                  j!                  j                  d        j"                  j!                  j                  d        j%                          d {    j                  rj                  j                  v rmj*                  sat        j                  dj                   d       j                  j                  = j"                  j!                  j                  d        	 d d d       d {    y 7 7 S# 1 sw Y   SxY w7 # t&        $ r& t        j)                  dj                   d       Y w xY w# j                  rj                  j                  v rnj*                  sat        j                  dj                   d       j                  j                  = j"                  j!                  j                  d        w w w w xY w7 # 1 d {  7  sw Y   y xY ww)NFrF   zSession z idle timeoutz crashedzCleaning up crashed session z from active instances.)rG   rH   r$   CancelScoper#   current_timedeadline
idle_scoper   r8   rI   cancelled_caughtrA   r2   r3   r'   popr(   rU   rJ   rK   is_terminated)rD   rL   rM   rN   rf   rO   r,   s        r-   
run_serverzIStreamableHTTPSessionManager._handle_stateful_request.<locals>.run_server  s    -557774;1\#++-&^
 */):):)<J#88D6;6H6H6JTMfMf6f
 3<F 9!+&*hhll$/$0$(HH$J$J$L.3	 '3 '" !" !" ",  *::'5'D'D'P P'P &h~7T7T6UUb,c d $ 6 6 : :>;X;XZ^ _ $ 4 4 8 89V9VX\ ]&4&>&>&@ @ @
 !/ = =$2$A$ATE[E[$[(6(D(D &$B'5'D'D&E F8%8!"
 %)$:$:>;X;X$Y $ 4 4 8 89V9VX\ ]S  877!" ", !A( a",,x8U8U7VV^-_`a !/ = =$2$A$ATE[E[$[(6(D(D &$B'5'D'D&E F8%8!"
 %)$:$:>;X;X$Y $ 4 4 8 89V9VX\ ] )E %\ !>?  8777s   LHLK.AH";H=H>HB$H"&H 'H"+BK.<LK,LHH		H"",IIIIBK))K.,L.L 4K75L <L)rD   rQ   returnNone)$r   headersgetr   
isinstancer   r   r'   r(   r2   warningr   r   r   r	   model_dump_jsonrR   rf   r#   r$   rd   re   r?   r&   r   hexr   r   r   r!   r"   rA   r3   rS   r)   rT   )r,   r<   r=   r>   requestrequest_mcp_session_idrW   	requestor	transportbodyresponsenew_session_idrj   rO   s   `            @r-   r;   z5StreamableHTTPSessionManager._handle_stateful_request   s     %)!(!4!45J!Kyy 3=dDU3V)$/\`	 "-2HDLbLb2b../EFID00445KLL r*3B/ $!nI?dw<x $(($T(J #1
 ugt444LLLM##/D4M4M4Y050B0B0DtG`G`0`	$$-**5'4@@@!)LL12222!&!>#1-1-?-? $ 0 0&*&<&<#'#6#6" &44@@@(JSD(()F)FGHV&&~'D'DEEnEUVW INHaHa *^ *^Z ''333&&,,Z888 %33E7DIIIG 322L  .	`s8tD  $$d$FTWdvH 5'4000q 5 A 3@ 9 JG 3222X 1s   C;L;?L A8L;8L9-L;&L'L;*C+L$LL$0L 1L$5L; L"AL;L9L;L;L;L$ L$"L;$L6*L-+L62L;)NFFNNN)r   zMCPServer[Any, Any]r   zEventStore | Noner   boolr    rz   r!   z TransportSecuritySettings | Noner"   z
int | Noner#   zfloat | None)rk   zAsyncIterator[None])r<   r   r=   r
   r>   r   rk   rl   )__name__
__module____qualname____doc__r.   
contextlibasynccontextmanagerr8   r?   r:   r;    r/   r-   r   r      s    J *.#>B%)-1"" "" '"" 	""
 "" <"" #"" +""H ##'- $'-RFF F 	F
 
F2/)/) /) 	/)
 
/)b~1~1 ~1 	~1
 
~1r/   r   )+r~   
__future__r   r   loggingcollections.abcr   typingr   uuidr   r$   	anyio.abcr   starlette.requestsr   starlette.responsesr	   starlette.typesr
   r   r   &mcp.server.auth.middleware.bearer_authr   r   r   mcp.server.lowlevel.serverr   	MCPServermcp.server.streamable_httpr   r   r   mcp.server.transport_securityr   	mcp.typesr   r   r   	getLoggerr{   r2   r   r   r/   r-   <module>r      si    5 "   )      & ( 0 0 q q : 
 D > >			8	$y1 y1r/   