§
    ­eîi‰7  ã                  ó  — 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  ddl!m"Z"m#Z#m$Z$  ej%        e&¦  «        Z' G d„ d¦  «        Z(dS )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)ÚTransportSecuritySettings)ÚINVALID_REQUESTÚ	ErrorDataÚJSONRPCErrorc                  ób   — e Zd ZdZ	 	 	 	 	 	 dd d„Zej        d!d„¦   «         Zd"d„Zd"d„Z	d"d„Z
dS )#Ú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.
    NFÚappúMCPServer[Any, Any]Úevent_storeúEventStore | NoneÚjson_responseÚboolÚ	statelessÚsecurity_settingsú TransportSecuritySettings | NoneÚretry_intervalú
int | NoneÚsession_idle_timeoutúfloat | Nonec                óF  — |�|dk    rt          d¦  «        ‚|r|�t          d¦  «        ‚|| _        || _        || _        || _        || _        || _        || _        t          j
        ¦   «         | _        i | _        d | _        t          j
        ¦   «         | _        d| _        d S )Nr   z9session_idle_timeout must be a positive number of secondsz7session_idle_timeout is not supported in stateless modeF)Ú
ValueErrorÚRuntimeErrorr   r   r   r   r   r!   r#   ÚanyioÚLockÚ_session_creation_lockÚ_server_instancesÚ_task_groupÚ	_run_lockÚ_has_started)Úselfr   r   r   r   r   r!   r#   s           úf/home/div/.hermes/hermes-agent/venv/lib/python3.11/site-packages/mcp/server/streamable_http_manager.pyÚ__init__z%StreamableHTTPSessionManager.__init__A   sµ   € ð  Ð+Ð0DÈÒ0IÐ0IÝÐXÑYÔYÐYØð 	ZÐ-Ð9ÝÐXÑYÔYÐYàˆŒØ&ˆÔØ*ˆÔØ"ˆŒØ!2ˆÔØ,ˆÔØ$8ˆÔ!õ ',¤j¡l¤lˆÔ#ØKMˆÔð  ˆÔåœ™œˆŒØ!ˆÔÐÐó    ÚreturnúAsyncIterator[None]c               óÊ  K  — | j         4 ƒd{V —† | j        rt          d¦  «        ‚d| _        ddd¦  «        ƒd{V —† n# 1 ƒd{V —†swxY w Y   t          j        ¦   «         4 ƒd{V —†}|| _        t                               d¦  «         	 dW V — t                               d¦  «         |j         	                    ¦   «          d| _        | j
                             ¦   «          nX# t                               d¦  «         |j         	                    ¦   «          d| _        | j
                             ¦   «          w xY w	 ddd¦  «        ƒd{V —† dS # 1 ƒd{V —†swxY w Y   dS )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+   Úclear)r/   Útgs     r0   Úrunz StreamableHTTPSessionManager.runb   sz  è è € ð& ”>ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%ØÔ ð Ý"ðYñô ð ð !%ˆDÔð	%ð 	%ð 	%ñ 	%ô 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%øøøð 	%ð 	%ð 	%ð 	%õ Ô*Ñ,Ô,ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/°à!ˆDÔÝ�KŠKÐ@ÑAÔAÐAð/Ø����å—’ÐJÑKÔKÐKà”×&Ò&Ñ(Ô(Ð(Ø#'�Ô àÔ&×,Ò,Ñ.Ô.Ð.Ð.øõ —’ÐJÑKÔKÐKà”×&Ò&Ñ(Ô(Ð(Ø#'�Ô àÔ&×,Ò,Ñ.Ô.Ð.Ð.øøøÐ.ð	/ð 	/ð 	/ñ 	/ô 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/øøøð 	/ð 	/ð 	/ð 	/ð 	/ð 	/s=   �A Á 
A
ÁA
Á*"EÂC&ÂAEÃ&AD;Ä;EÅ
EÅEÚscoper   Úreceiver   Úsendr   ÚNonec              ƒ  ó¼   K  — | j         €t          d¦  «        ‚| j        r|                      |||¦  «        ƒ d{V —† dS |                      |||¦  «        ƒ d{V —† dS )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/   r>   r?   r@   s       r0   Úhandle_requestz+StreamableHTTPSessionManager.handle_request‹   s�   è è € ð  ÔÐ#ÝÐWÑXÔXÐXð Œ>ð 	FØ×0Ò0°¸ÀÑFÔFÐFÐFÐFÐFÐFÐFÐFÐFÐFà×/Ò/°°wÀÑEÔEÐEÐEÐEÐEÐEÐEÐEÐEÐEr2   c              ƒ  ód  ‡ ‡K  — t                                d¦  «         t          d‰ j        d‰ j        ¬¦  «        Št
          j        dœdˆˆ fd„}‰ j        €J ‚‰ j                             |¦  «        ƒ d{V —† ‰ 	                    |||¦  «        ƒ d{V —† ‰ 
                    ¦   «         ƒ d{V —† dS )	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_statusrJ   úTaskStatus[None]c              “  óŠ  •K  — ‰                      ¦   «         4 ƒd {V —†}|\  }}|                      ¦   «          	 ‰j                             ||‰j                             ¦   «         d¬¦  «        ƒ d {V —† n*# t
          $ r t                               d¦  «         Y nw xY wd d d ¦  «        ƒd {V —† d S # 1 ƒd {V —†swxY w Y   d S )NT©r   zStateless session crashed)ÚconnectÚstartedr   r=   Úcreate_initialization_optionsÚ	Exceptionr7   Ú	exception)rJ   ÚstreamsÚread_streamÚwrite_streamÚhttp_transportr/   s       €€r0   Úrun_stateless_serverzTStreamableHTTPSessionManager._handle_stateless_request.<locals>.run_stateless_server¼   s±  øè è € Ø%×-Ò-Ñ/Ô/ð Bð Bð Bð Bð Bð Bð B°7Ø,3Ñ)�˜\Ø×#Ò#Ñ%Ô%Ð%ðBØœ(Ÿ,š,Ø#Ø$Øœ×>Ò>Ñ@Ô@Ø"&ð	 'ñ ô ð ð ð ð ð ð ð ð øõ !ð Bð Bð BÝ×$Ò$Ð%@ÑAÔAÐAÐAÐAðBøøøðBð Bð Bñ Bô Bð Bð Bð Bð Bð Bð Bð Bð Bð Bð Bð Bð Bð Bð Bð Bð Bð Bð Bð Bøøøð Bð Bð Bð Bð Bð Bs4   žB2¹;A5Á4B2Á5$BÂB2ÂBÂB2Â2
B<Â?B<)rJ   rK   )r7   Údebugr   r   r   r(   ÚTASK_STATUS_IGNOREDr,   ÚstartrE   Ú	terminate)r/   r>   r?   r@   rW   rV   s   `    @r0   rC   z6StreamableHTTPSessionManager._handle_stateless_request¤   s  øøè è € õ 	�ŠÐNÑOÔOÐOå6ØØ%)Ô%7ØØ"Ô4ð	
ñ 
ô 
ˆõ KPÔJcð 	Bð 	Bð 	Bð 	Bð 	Bð 	Bð 	Bð 	Bð 	Bð ÔÐ+Ð+Ð+àÔ×$Ò$Ð%9Ñ:Ô:Ð:Ð:Ð:Ð:Ð:Ð:Ð:ð ×+Ò+¨E°7¸DÑAÔAÐAÐAÐAÐAÐAÐAÐAð ×&Ò&Ñ(Ô(Ð(Ð(Ð(Ð(Ð(Ð(Ð(Ð(Ð(r2   c              ƒ  ó~  ‡ ‡K  — t          ||¦  «        }|j                             t          ¦  «        }|�‚|‰ j        v ry‰ j        |         }t
                               d¦  «         |j        �,‰ j        �%t          j
        ¦   «         ‰ j        z   |j        _        |                     |||¦  «        ƒ d{V —† dS |�€t
                               d¦  «         ‰ j        4 ƒd{V —† t          ¦   «         j        }t!          |‰ j        ‰ j        ‰ j        ‰ j        ¬¦  «        Š‰j        €J ‚‰‰ j        ‰j        <   t
                               d|› �¦  «         t          j        dœdˆˆ fd„}‰ j        €J ‚‰ j                             |¦  «        ƒ d{V —† ‰                     |||¦  «        ƒ d{V —† ddd¦  «        ƒd{V —† dS # 1 ƒd{V —†swxY w Y   dS t5          ddt7          t8          d¬¦  «        ¬¦  «        }	t;          |	                     dd¬¦  «        t>          j         d¬¦  «        }
 |
|||¦  «        ƒ d{V —† dS )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 transport)rG   rH   r   r   r!   z'Created new transport with session ID: rI   rJ   rK   r3   rA   c              “  ój  •K  — ‰                      ¦   «         4 ƒd {V —†}|\  }}|                      ¦   «          	 t          j        ¦   «         }‰j        �'t          j        ¦   «         ‰j        z   |_        |‰_        |5  ‰j         	                    ||‰j         
                    ¦   «         d¬¦  «        ƒ d {V —† d d d ¦  «         n# 1 swxY w Y   |j        rf‰j        €J ‚t                               d‰j        › d�¦  «         ‰j                             ‰j        d ¦  «         ‰                     ¦   «         ƒ d {V —† n3# t$          $ r& t                               d‰j        › d�¦  «         Y nw xY w‰j        rE‰j        ‰j        v r7‰j        s0t                               d‰j        › d�¦  «         ‰j        ‰j        = nQ# ‰j        rE‰j        ‰j        v r7‰j        s0t                               d‰j        › d�¦  «         ‰j        ‰j        = w xY w	 d d d ¦  «        ƒd {V —† d S # 1 ƒd {V —†swxY w Y   d S )NFrM   zSession z idle timeoutz crashedzCleaning up crashed session z from active instances.)rN   rO   r(   ÚCancelScoper#   Úcurrent_timeÚdeadlineÚ
idle_scoper   r=   rP   Úcancelled_caughtrG   r7   r8   r+   Úpopr[   rQ   rR   Úis_terminated)rJ   rS   rT   rU   ra   rV   r/   s        €€r0   Ú
run_serverzIStreamableHTTPSessionManager._handle_stateful_request.<locals>.run_server  s¹  øè è € Ø-×5Ò5Ñ7Ô7ð 'Zð 'Zð 'Zð 'Zð 'Zð 'Zð 'Z¸7Ø4;Ñ1˜ \Ø#×+Ò+Ñ-Ô-Ð-ð$Zõ
 */Ô):Ñ)<Ô)<˜JØ#Ô8ÐDÝ6;Ô6HÑ6JÔ6JÈTÔMfÑ6f 
Ô 3Ø<F Ô 9à!+ð "ð "Ø&*¤h§l¢lØ$/Ø$0Ø$(¤H×$JÒ$JÑ$LÔ$LØ.3ð	 '3ñ '"ô '"ð !"ð !"ð !"ð !"ð !"ð !"ð !"ð"ð "ð "ñ "ô "ð "ð "ð "ð "ð "ð "øøøð "ð "ð "ð "ð  *Ô:ð AØ'5Ô'DÐ'PÐ'PÐ'PÝ &§¢Ð,c°~Ô7TÐ,cÐ,cÐ,cÑ dÔ dÐ dØ $Ô 6× :Ò :¸>Ô;XÐZ^Ñ _Ô _Ð _Ø&4×&>Ò&>Ñ&@Ô&@Ð @Ð @Ð @Ð @Ð @Ð @Ð @øøÝ(ð að að aÝ"×,Ò,Ð-_¸Ô8UÐ-_Ð-_Ð-_Ñ`Ô`Ð`Ð`Ð`ðaøøøð !/Ô =ð
Zà$2Ô$AÀTÔE[Ð$[Ð$[Ø(6Ô(Dð %\õ !'§¢ð%8Ø'5Ô'Dð%8ð %8ð %8ñ!"ô !"ð !"ð
 %)Ô$:¸>Ô;XÐ$Yøøð !/Ô =ð
Zà$2Ô$AÀTÔE[Ð$[Ð$[Ø(6Ô(Dð %\õ !'§¢ð%8Ø'5Ô'Dð%8ð %8ð %8ñ!"ô !"ð !"ð
 %)Ô$:¸>Ô;XÐ$YÐ YÐ YÐ YÐ YÐ YðO'Zð 'Zð 'Zñ 'Zô 'Zð 'Zð 'Zð 'Zð 'Zð 'Zð 'Zð 'Zð 'Zð 'Zð 'Zð 'Zð 'Zð 'Zð 'Zð 'Zð 'Zð 'Zð 'Zð 'Zøøøð 'Zð 'Zð 'Zð 'Zð 'Zð 'Zsn   žH"¹AD=Á<<CÂ8D=ÃC	ÃD=ÃC	ÃA0D=Ä<F=Ä=-E-Å*F=Å,E-Å-F=Å0AH"Æ=AHÈH"È"
H,È/H,z2.0zserver-errorzSession not found)ÚcodeÚmessage)ÚjsonrpcÚidÚerrorT)Úby_aliasÚexclude_nonezapplication/json)ÚcontentÚstatus_codeÚ
media_type)rJ   rK   r3   rA   )!r	   ÚheadersÚgetr   r+   r7   rX   ra   r#   r(   r_   r`   rE   r*   r   Úhexr   r   r   r   r!   rG   r8   rY   r,   rZ   r   r   r   r
   Úmodel_dump_jsonr   Ú	NOT_FOUND)r/   r>   r?   r@   ÚrequestÚrequest_mcp_session_idÚ	transportÚnew_session_idre   Úerror_responseÚresponserV   s   `          @r0   rD   z5StreamableHTTPSessionManager._handle_stateful_requestÕ   s€  øøè è € õ ˜% Ñ)Ô)ˆØ!(¤×!4Ò!4Õ5JÑ!KÔ!KÐð "Ð-Ð2HÈDÔLbÐ2bÐ2bØÔ.Ð/EÔFˆIÝ�LŠLÐLÑMÔMÐMàÔ#Ð/°DÔ4MÐ4YÝ05Ô0BÑ0DÔ0DÀtÔG`Ñ0`�	Ô$Ô-Ø×*Ò*¨5°'¸4Ñ@Ô@Ð@Ð@Ð@Ð@Ð@Ð@Ð@ØˆFà!Ñ)å�LŠLÐ1Ñ2Ô2Ð2ØÔ2ð ?Jð ?Jð ?Jð ?Jð ?Jð ?Jð ?Jð ?JÝ!&¡¤¤�Ý!>Ø#1Ø-1Ô-?Ø $Ô 0Ø&*Ô&<Ø#'Ô#6ð"ñ "ô "�ð &Ô4Ð@Ð@Ð@ØHV�Ô& ~Ô'DÑEÝ—’ÐVÀnÐVÐVÑWÔWÐWõ INÔHað (Zð (Zð (Zð (Zð (Zð (Zð (Zð (Zð (ZðV Ô'Ð3Ð3Ð3àÔ&×,Ò,¨ZÑ8Ô8Ð8Ð8Ð8Ð8Ð8Ð8Ð8ð %×3Ò3°E¸7ÀDÑIÔIÐIÐIÐIÐIÐIÐIÐIð?Jð ?Jð ?Jñ ?Jô ?Jð ?Jð ?Jð ?Jð ?Jð ?Jð ?Jð ?Jð ?Jð ?Jð ?Jð ?Jð ?Jð ?Jð ?Jð ?Jð ?Jð ?Jð ?Jð ?Jøøøð ?Jð ?Jð ?Jð ?Jð ?Jð ?JõH *ØØ!ÝÝ(Ø/ðñ ô ðñ ô ˆNõ  Ø&×6Ò6ÀÐSWÐ6ÑXÔXÝ&Ô0Ø-ðñ ô ˆHð
 �(˜5 '¨4Ñ0Ô0Ð0Ð0Ð0Ð0Ð0Ð0Ð0Ð0Ð0s   Ã"CG Ç 
G
ÇG
)NFFNNN)r   r   r   r   r   r   r   r   r   r    r!   r"   r#   r$   )r3   r4   )r>   r   r?   r   r@   r   r3   rA   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r1   Ú
contextlibÚasynccontextmanagerr=   rE   rC   rD   © r2   r0   r   r      s¹   € € € € € ð ð  ðJ *.Ø#ØØ>BØ%)Ø-1ð"ð "ð "ð "ð "ðB Ô#ð&/ð &/ð &/ñ $Ô#ð&/ðPFð Fð Fð Fð2/)ð /)ð /)ð /)ðbo1ð o1ð o1ð o1ð o1ð o1r2   r   ))r~   Ú
__future__r   r   Ú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   Ú	mcp.typesr   r   r   Ú	getLoggerr{   r7   r   r�   r2   r0   ú<module>r’      sœ  ðØ 5Ð 5à "Ð "Ð "Ð "Ð "Ð "à Ð Ð Ð Ø €€€Ø )Ð )Ð )Ð )Ð )Ð )Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð à €€€Ø  Ð  Ð  Ð  Ð  Ð  Ø &Ð &Ð &Ð &Ð &Ð &Ø (Ð (Ð (Ð (Ð (Ð (Ø 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0à :Ð :Ð :Ð :Ð :Ð :ðð ð ð ð ð ð ð ð ð ð
 DÐ CÐ CÐ CÐ CÐ CØ >Ð >Ð >Ð >Ð >Ð >Ð >Ð >Ð >Ð >à	ˆÔ	˜8Ñ	$Ô	$€ðf1ð f1ð f1ð f1ð f1ñ f1ô f1ð f1ð f1ð f1r2   