o
    ho                     @   s  d Z ddlZddlZddlZddlmZmZ ddlmZm	Z	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mZ dd	lm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# ddl$m%Z%m&Z& ddl'm(Z( ddl)m*Z*m+Z+m,Z,m-Z-m.Z.m/Z/m0Z0m1Z1m2Z2m3Z3m4Z4 e5e6Z7dZ8dZ9dZ:dZ;dZ<dZ=e>dZ?e@ZAe@ZBeG dd dZCe
eCge	d f ZDG dd deZEG dd dZFdS )z
StreamableHTTP Server Transport Module

This module implements an HTTP transport layer with Streamable HTTP.

The transport handles bidirectional communication using HTTP requests and
responses, with streaming support for long-running operations.
    N)ABCabstractmethod)AsyncGenerator	AwaitableCallable)asynccontextmanager)	dataclass)
HTTPStatus)MemoryObjectReceiveStreamMemoryObjectSendStream)ValidationError)EventSourceResponse)Request)Response)ReceiveScopeSend)TransportSecurityMiddlewareTransportSecuritySettings)ServerMessageMetadataSessionMessage)SUPPORTED_PROTOCOL_VERSIONS)DEFAULT_NEGOTIATED_VERSIONINTERNAL_ERRORINVALID_PARAMSINVALID_REQUESTPARSE_ERROR	ErrorDataJSONRPCErrorJSONRPCMessageJSONRPCRequestJSONRPCResponse	RequestIdzmcp-session-idzmcp-protocol-versionzlast-event-idzapplication/jsonztext/event-stream_GET_streamz^[\x21-\x7E]+$c                   @   s*   e Zd ZU dZeed< dZedB ed< dS )EventMessagezM
    A JSONRPCMessage with an optional event ID for stream resumability.
    messageNevent_id)__name__
__module____qualname____doc__r   __annotations__r&   str r-   r-   [/var/www/html/openai_agents/venv/lib/python3.10/site-packages/mcp/server/streamable_http.pyr$   G   s   
 r$   c                   @   sH   e Zd ZdZedededefddZedede	ded	B fd
dZ
d	S )
EventStorez?
    Interface for resumability support via event storage.
    	stream_idr%   returnc                       dS )z
        Stores an event for later retrieval.

        Args:
            stream_id: ID of the stream the event belongs to
            message: The JSON-RPC message to store

        Returns:
            The generated event ID for the stored event
        Nr-   )selfr0   r%   r-   r-   r.   store_eventY   s   zEventStore.store_eventlast_event_idsend_callbackNc                    r2   )a2  
        Replays events that occurred after the specified event ID.

        Args:
            last_event_id: The ID of the last event the client received
            send_callback: A callback function to send events to the client

        Returns:
            The stream ID of the replayed events
        Nr-   )r3   r5   r6   r-   r-   r.   replay_events_afterg   s   zEventStore.replay_events_after)r'   r(   r)   r*   r   StreamIdr   EventIdr4   EventCallbackr7   r-   r-   r-   r.   r/   T   s    r/   c                   @   s  e Zd ZU dZdZeeeB  dB ed< dZ	e
eeB  dB ed< dZee dB ed< dZe
e dB ed< eed< 			dBd	edB d
ededB dedB ddf
ddZedefddZedfdedededeeef dB def
ddZejdfdedB dedeeef dB defddZdededB fddZde deeef fdd Z!d!e"ddfd"d#Z#d$e$d%e%d&e&ddfd'd(Z'dede(eef fd)d*Z)dedefd+d,Z*d$e$ded%e%d&e&ddf
d-d.Z+ded&e&ddfd/d0Z,ded&e&ddfd1d2Z-dCd3d4Z.ded&e&ddfd5d6Z/ded&e&defd7d8Z0ded&e&defd9d:Z1ded&e&defd;d<Z2d=eded&e&ddfd>d?Z3e4de5e(e
eeB  ee f df fd@dAZ6dS )DStreamableHTTPServerTransportz
    HTTP server transport with event streaming support for MCP.

    Handles JSON-RPC messages in HTTP POST requests with SSE streaming.
    Supports optional JSON responses and session management.
    N_read_stream_writer_read_stream_write_stream_write_stream_reader	_securityFmcp_session_idis_json_response_enabledevent_storesecurity_settingsr1   c                 C   sF   |durt |std|| _|| _|| _t|| _i | _d| _	dS )a  
        Initialize a new StreamableHTTP server transport.

        Args:
            mcp_session_id: Optional session identifier for this connection.
                            Must contain only visible ASCII characters (0x21-0x7E).
            is_json_response_enabled: If True, return JSON responses for requests
                                    instead of SSE streams. Default is False.
            event_store: Event store for resumability support. If provided,
                        resumability will be enabled, allowing clients to
                        reconnect and resume messages.
            security_settings: Optional security settings for DNS rebinding protection.

        Raises:
            ValueError: If the session ID contains invalid characters.
        NzASession ID must only contain visible ASCII characters (0x21-0x7E)F)
SESSION_ID_PATTERN	fullmatch
ValueErrorrA   rB   _event_storer   r@   _request_streams_terminated)r3   rA   rB   rC   rD   r-   r-   r.   __init__   s   

z&StreamableHTTPServerTransport.__init__c                 C   s   | j S )z7Check if this transport has been explicitly terminated.)rJ   )r3   r-   r-   r.   is_terminated   s   z+StreamableHTTPServerTransport.is_terminatederror_messagestatus_code
error_codeheadersc                 C   sT   dt i}|r|| | jr| j|t< tddt||dd}t|jddd||dS )	z6Create an error response with a simple string message.Content-Typez2.0zserver-error)coder%   )jsonrpciderrorTby_aliasexclude_nonerN   rP   )CONTENT_TYPE_JSONupdaterA   MCP_SESSION_ID_HEADERr   r   r   model_dump_json)r3   rM   rN   rO   rP   response_headerserror_responser-   r-   r.   _create_error_response   s$   

	z4StreamableHTTPServerTransport._create_error_responseresponse_messagec                 C   sF   dt i}|r|| | jr| j|t< t|r|jdddnd||dS )z,Create a JSON response from a JSONRPCMessagerQ   TrV   NrY   )rZ   r[   rA   r\   r   r]   )r3   ra   rN   rP   r^   r-   r-   r.   _create_json_response   s   

z3StreamableHTTPServerTransport._create_json_responserequestc                 C   s   |j tS )z,Extract the session ID from request headers.)rP   getr\   )r3   rc   r-   r-   r.   _get_session_id   s   z-StreamableHTTPServerTransport._get_session_idevent_messagec                 C   s*   d|j jdddd}|jr|j|d< |S )z2Create event data dictionary from an EventMessage.r%   TrV   )eventdatarT   )r%   r]   r&   )r3   rf   
event_datar-   r-   r.   _create_event_data   s   
z0StreamableHTTPServerTransport._create_event_data
request_idc              
      s   || j v rLz=z| j | d  I dH  | j | d  I dH  W n ty/   td Y nw W | j |d dS W | j |d dS | j |d w dS )z/Clean up memory streams for a given request ID.r   N   z4Error closing memory streams - may already be closed)rI   aclose	Exceptionloggerdebugpop)r3   rk   r-   r-   r.   _clean_up_memory_streams   s   
"z6StreamableHTTPServerTransport._clean_up_memory_streamsscopereceivesendc                    s   t ||}|jdk}| jj||dI dH }|r#||||I dH  dS | jr8| dtj}||||I dH  dS |jdkrJ| ||||I dH  dS |jdkrZ| 	||I dH  dS |jdkrj| 
||I dH  dS | ||I dH  dS )z6Application entry point that handles all HTTP requestsPOST)is_postNz&Not Found: Session has been terminatedGETDELETE)r   methodr@   validate_requestrJ   r`   r	   	NOT_FOUND_handle_post_request_handle_get_request_handle_delete_request_handle_unsupported_request)r3   rs   rt   ru   rc   rw   r_   responser-   r-   r.   handle_request  s*   




z,StreamableHTTPServerTransport.handle_requestc                 C   sN   |j dd}dd |dD }tdd |D }tdd |D }||fS )	z6Check if the request accepts the required media types.accept c                 S      g | ]}|  qS r-   strip.0
media_typer-   r-   r.   
<listcomp>'      zGStreamableHTTPServerTransport._check_accept_headers.<locals>.<listcomp>,c                 s       | ]}| tV  qd S N)
startswithrZ   r   r-   r-   r.   	<genexpr>)      zFStreamableHTTPServerTransport._check_accept_headers.<locals>.<genexpr>c                 s   r   r   )r   CONTENT_TYPE_SSEr   r-   r-   r.   r   *  r   rP   rd   splitany)r3   rc   accept_headeraccept_typeshas_jsonhas_sser-   r-   r.   _check_accept_headers$  s
   z3StreamableHTTPServerTransport._check_accept_headersc                 C   s>   |j dd}dd |dd dD }tdd	 |D S )
z2Check if the request has the correct Content-Type.zcontent-typer   c                 S   r   r-   r   r   partr-   r-   r.   r   1  r   zEStreamableHTTPServerTransport._check_content_type.<locals>.<listcomp>;r   r   c                 s   s    | ]}|t kV  qd S r   )rZ   r   r-   r-   r.   r   3  s    zDStreamableHTTPServerTransport._check_content_type.<locals>.<genexpr>r   )r3   rc   content_typecontent_type_partsr-   r-   r.   _check_content_type.  s   z1StreamableHTTPServerTransport._check_content_typec              
      sZ  j }|du rtdze|\}}|r|s,dtj}||||I dH  W dS |sDdtj}||||I dH  W dS | I dH }	zt	
|	}
W n+ t	jy} } zdt| tjt}||||I dH  W Y d}~W dS d}~ww zt|
}W n* ty } zdt| tjt}||||I dH  W Y d}~W dS d}~ww t|jto|jjdk}|rjr|}|r|jkrdtj}||||I dH  W dS n||I dH sW dS t|jtsdtj}||||I dH  t|d	}t||d
}||I dH  W dS t|jj  t!j"t# dj$ < j$  d j%rt|d	}t||d
}||I dH  zzPd}2 z!3 dH W }t|j&jt't(B rc|j&} nt)*d|j&jj  qN6 |r|}||||I dH  nt)+d dtj,}||||I dH  W n  t-y   t).d dtj,t/}||||I dH  Y nw W 0 I dH  W dS W 0 I dH  W dS 0 I dH  w t!j"t1ttf  d\} fdd}ddt2djrt3jini }t4|||d}zBt!5 4 I dH &}|6|||| t|d	}t||d
}||I dH  W d  I dH  n1 I dH s?w   Y  W W dS W W dS  t-yr   t).d 7 I dH  |7 I dH  0 I dH  Y W dS w  t-y } z-t).d d| tj,t/}||||I dH  |r|t-|I dH  W Y d}~dS d}~ww )z2Handle POST requests containing JSON-RPC messages.NBNo read stream writer available. Ensure connect() is called first.zNNot Acceptable: Client must accept both application/json and text/event-streamz=Unsupported Media Type: Content-Type must be application/jsonzParse error: zValidation error: 
initialize(Not Found: Invalid or expired session ID)request_context)metadatar   rl   z
received: z1No response message received before stream closedz.Error processing request: No response receivedzError processing JSON responsezError processing requestc               
      s6  zz\4 I d H I 4 I d H - 2 z3 d H W }  | }|I d H  t| jjttB r1 nq6 W d   I d H  n1 I d H sCw   Y  W d   I d H  n1 I d H sXw   Y  W n tyl   t	d Y nw W t
d  I d H  d S W t
d  I d H  d S t
d  I d H  w )NzError in SSE writerzClosing SSE writer)rj   ru   
isinstancer%   rootr!   r   rn   ro   	exceptionrp   rr   rf   ri   rk   request_stream_readerr3   sse_stream_writerr-   r.   
sse_writer  s4   
R


zFStreamableHTTPServerTransport._handle_post_request.<locals>.sse_writerno-cache, no-transform
keep-alivezCache-Control
ConnectionrQ   contentdata_sender_callablerP   zSSE response errorzError handling POST requestzError handling POST request: )8r<   rG   r   r`   r	   NOT_ACCEPTABLEr   UNSUPPORTED_MEDIA_TYPEbodyjsonloadsJSONDecodeErrorr,   BAD_REQUESTr   r   model_validater   r   r   r   r    rz   rA   re   r|   _validate_request_headersrb   ACCEPTEDr   r   ru   rT   anyiocreate_memory_object_streamr$   rI   rB   r%   r!   r   ro   rp   rU   INTERNAL_SERVER_ERRORrn   r   r   rr   dictr   r\   r   create_task_group
start_soonrm   )r3   rs   rc   rt   ru   writerr   r   r   r   raw_messageer%   is_initialization_requestrequest_session_idr   session_messagera   rf   sse_stream_readerr   rP   tgerrr-   r   r.   r}   5  s  








	(
:

z2StreamableHTTPServerTransport._handle_post_requestc                    s|   j }|du rtd |\}}|s) dtj}||j|j|I dH  dS  ||I dH s4dS |j	
t }rH |||I dH  dS ddtd} jrV j|t< t jv ro dtj}||j|j|I dH  dS tjtttf  d\}	 fd	d
}
t|	|
|d}z||j|j|I dH  W dS  ty   td  I dH  |	 I dH   tI dH  Y dS w )a   
        Handle GET request to establish SSE.

        This allows the server to communicate to the client without the client
        first sending data via HTTP POST. The server can send JSON-RPC requests
        and notifications on this stream.
        Nr   z4Not Acceptable: Client must accept text/event-streamr   r   r   z4Conflict: Only one SSE stream is allowed per sessionr   c               
      sB  zzbt jt d jt<  jt d } 4 I d H > | 4 I d H " | 2 z3 d H W } |}|I d H  q$6 W d   I d H  n1 I d H sIw   Y  W d   I d H  n1 I d H s^w   Y  W n tyr   t	d Y nw W t
d  tI d H  d S W t
d  tI d H  d S t
d  tI d H  w )Nr   rl   zError in standalone SSE writerzClosing standalone SSE writer)r   r   r$   rI   GET_STREAM_KEYrj   ru   rn   ro   r   rp   rr   )standalone_stream_readerrf   ri   r3   r   r-   r.   standalone_sse_writer0  s,   
R


zPStreamableHTTPServerTransport._handle_get_request.<locals>.standalone_sse_writerr   z Error in standalone SSE response)r<   rG   r   r`   r	   r   rs   rt   r   rP   rd   LAST_EVENT_ID_HEADER_replay_eventsr   rA   r\   r   rI   CONFLICTr   r   r   r,   r   rn   ro   r   rm   rr   )r3   rc   ru   r   _r   r   r5   rP   r   r   r-   r   r.   r~     sZ   


z1StreamableHTTPServerTransport._handle_get_requestc                    s|   | j s| dtj}||j|j|I dH  dS | ||I dH s#dS |  I dH  | dtj	}||j|j|I dH  dS )z8Handle DELETE requests for explicit session termination.z5Method Not Allowed: Session termination not supportedN)
rA   r`   r	   METHOD_NOT_ALLOWEDrs   rt   r   	terminaterb   OK)r3   rc   ru   r   r-   r-   r.   r   X  s    z4StreamableHTTPServerTransport._handle_delete_requestc              
      s   d| _ td| j  t| j }|D ]
}| |I dH  q| j  z:| j	dur4| j	
 I dH  | jdurA| j
 I dH  | jdurN| j
 I dH  | jdur^| j
 I dH  W dS W dS  ty{ } ztd|  W Y d}~dS d}~ww )zTerminate the current session, closing all streams.

        Once terminated, all requests with this session ID will receive 404 Not Found.
        TzTerminating session: NError closing streams: )rJ   ro   inforA   listrI   keysrr   clearr<   rm   r=   r?   r>   rn   rp   )r3   request_stream_keyskeyr   r-   r-   r.   r   o  s*   




z'StreamableHTTPServerTransport.terminatec                    sH   t dd}| jr| j|t< | jdtj|d}||j|j|I dH  dS )z Handle unsupported HTTP methods.zGET, POST, DELETE)rQ   AllowzMethod Not Allowed)rP   N)rZ   rA   r\   r`   r	   r   rs   rt   )r3   rc   ru   rP   r   r-   r-   r.   r     s   
z9StreamableHTTPServerTransport._handle_unsupported_requestc                    s2   |  ||I d H sdS | ||I d H sdS dS )NFT)_validate_session_validate_protocol_version)r3   rc   ru   r-   r-   r.   r     s   z7StreamableHTTPServerTransport._validate_request_headersc                    sx   | j sdS | |}|s!| dtj}||j|j|I dH  dS || j kr:| dtj}||j|j|I dH  dS dS )z'Validate the session ID in the request.TzBad Request: Missing session IDNFr   )rA   re   r`   r	   r   rs   rt   r|   )r3   rc   ru   r   r   r-   r-   r.   r     s&   

z/StreamableHTTPServerTransport._validate_sessionc                    sj   |j t}|du rt}|tvr3dt}| d| dd|  tj}||j	|j
|I dH  dS dS )z4Validate the protocol version header in the request.Nz, z+Bad Request: Unsupported protocol version: z. zSupported versions: FT)rP   rd   MCP_PROTOCOL_VERSION_HEADERr   r   joinr`   r	   r   rs   rt   )r3   rc   ru   protocol_versionsupported_versionsr   r-   r-   r.   r     s   

z8StreamableHTTPServerTransport._validate_protocol_versionr5   c                    sL  j   sdS z{ddtd}jrj|t< tjtttf  d\} fdd}t|||d}z@z||j	|j
|I dH  W n tyP   td	 Y nw W  I dH  | I dH  W dS W  I dH  | I dH  W dS  I dH  | I dH  w  ty   td
 d
tjt}||j	|j
|I dH  Y dS w )z
        Replays events that would have been sent after the specified event ID.
        Only used when resumability is enabled.
        Nr   r   r   r   c               
      s$  z4 I d H m dt dd ffdd}  | I d H }|ri|jvritjt  dj|< j| d }|4 I d H " |2 z3 d H W }|}|I d H  q?6 W d   I d H  n1 I d H sdw   Y  W d   I d H  W d S 1 I d H s{w   Y  W d S  ty   t	d Y d S w )Nrf   r1   c                    s      | }|I d H  d S r   )rj   ru   r   r   r-   r.   
send_event  s   
zWStreamableHTTPServerTransport._replay_events.<locals>.replay_sender.<locals>.send_eventr   rl   zError in replay sender)
r$   r7   rI   r   r   rj   ru   rn   ro   r   )r   r0   
msg_readerrf   ri   rC   r5   r3   r   r-   r.   replay_sender  s$   
*2zCStreamableHTTPServerTransport._replay_events.<locals>.replay_senderr   zError in replay responsezError replaying events)rH   r   rA   r\   r   r   r   r,   r   rs   rt   rn   ro   r   rm   r`   r	   r   r   )r3   r5   rc   ru   rP   r   r   r   r-   r   r.   r     sP   

z,StreamableHTTPServerTransport._replay_eventsc                   s   t jttB  d\}}t jt d\}| _| _ _| _t  4 I dH } fdd}|	| zV||fV  W t
 j D ]
} |I dH  qD j  z| I dH  | I dH   I dH  | I dH  W nj ty } ztd|  W Y d}~n`d}~ww t
 j D ]
} |I dH  q j  z| I dH  | I dH   I dH  | I dH  W w  ty } ztd|  W Y d}~w d}~ww W d  I dH  dS W d  I dH  dS 1 I dH sw   Y  dS )zContext manager that provides read and write streams for a connection.

        Yields:
            Tuple of (read_stream, write_stream) for bidirectional communication
        r   Nc               
      sD  z2 z3 d H W } | j }d }t|jttB r t|jj}|}n| jd ur7t| jtr7| jj	d ur7t| jj	}|d ur=|nt
}d } jrY j||I d H }td| d|  | jv rz j| d t||I d H  W q tjtjfy    j|d  Y qw td| d q6 W d S  ty   td Y d S w )NzStored z from r   zRequest stream z not found 
                                for message. Still processing message as the client
                                might reconnect and replay.zError in message router)r%   r   r   r!   r   r,   rT   r   r   related_request_idr   rH   r4   ro   rp   rI   ru   r$   r   BrokenResourceErrorClosedResourceErrorrq   loggingrn   r   )r   r%   target_request_idresponse_idrequest_stream_idr&   r3   write_stream_readerr-   r.   message_router=  sJ   

$

/z=StreamableHTTPServerTransport.connect.<locals>.message_routerr   )r   r   r   rn   r<   r=   r?   r>   r   r   r   rI   r   rr   r   rm   ro   rp   )r3   read_stream_writerread_streamwrite_streamr   r   r0   r   r-   r   r.   connect  sP   
5

I.z%StreamableHTTPServerTransport.connect)FNN)r1   N)7r'   r(   r)   r*   r<   r   r   rn   r+   r=   r
   r>   r?   r   r,   boolr/   r   rK   propertyrL   r   r	   intr   r   r`   r   r   rb   r   re   r$   rj   r"   rr   r   r   r   r   tupler   r   r}   r~   r   r   r   r   r   r   r   r   r   r   r-   r-   r-   r.   r;   z   s   
 
'
"

 H\
G
r;   )Gr*   r   r   reabcr   r   collections.abcr   r   r   
contextlibr   dataclassesr   httpr	   r   anyio.streams.memoryr
   r   pydanticr   sse_starletter   starlette.requestsr   starlette.responsesr   starlette.typesr   r   r   mcp.server.transport_securityr   r   mcp.shared.messager   r   mcp.shared.versionr   	mcp.typesr   r   r   r   r   r   r   r   r    r!   r"   	getLoggerr'   ro   r\   r   r   rZ   r   r   compilerE   r,   r8   r9   r$   r:   r/   r;   r-   r-   r-   r.   <module>   sF    	4

	&