o
    hM                     @   s  d Z ddl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ZddlmZ ddlmZ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mZm Z m!Z!m"Z"m#Z#m$Z$m%Z% e&e'Z(ee)B Z*ee* Z+ee Z,eg e-dB f Z.dZ/dZ0dZ1dZ2dZ3dZ4dZ5G dd de)Z6G dd de6Z7e	G dd dZ8G dd dZ9eddddedfde-d e:e-e-f dB d!e;eB d"e;eB d#e<d$ed%ej=dB d&ee>eee)B  ee e.f df fd'd(Z?dS ))z
StreamableHTTP Client Transport Module

This module implements the StreamableHTTP transport for MCP clients,
providing support for HTTP POST requests with optional SSE streaming responses
and session management.
    N)AsyncGenerator	AwaitableCallable)asynccontextmanager)	dataclass)	timedelta)	TaskGroup)MemoryObjectReceiveStreamMemoryObjectSendStream)EventSourceServerSentEventaconnect_sse)McpHttpClientFactorycreate_mcp_http_client)ClientMessageMetadataSessionMessage)	ErrorDataInitializeResultJSONRPCErrorJSONRPCMessageJSONRPCNotificationJSONRPCRequestJSONRPCResponse	RequestIdzmcp-session-idzmcp-protocol-versionzlast-event-idzcontent-typeacceptzapplication/jsonztext/event-streamc                   @      e Zd ZdZdS )StreamableHTTPErrorz3Base exception for StreamableHTTP transport errors.N__name__
__module____qualname____doc__ r"   r"   [/var/www/html/openai_agents/venv/lib/python3.10/site-packages/mcp/client/streamable_http.pyr   5       r   c                   @   r   )ResumptionErrorz*Raised when resumption request is invalid.Nr   r"   r"   r"   r#   r%   9   r$   r%   c                   @   s\   e Zd ZU dZejed< eeef ed< edB ed< e	ed< e
dB ed< eed< eed	< dS )
RequestContextz Context for a request operation.clientheadersN
session_idsession_messagemetadataread_stream_writersse_read_timeout)r   r   r    r!   httpxAsyncClient__annotations__dictstrr   r   StreamWriterfloatr"   r"   r"   r#   r&   =   s   
 
r&   c                   @   s  e Zd ZdZ				d>dedeeef dB deeB deeB d	ej	dB d
dfddZ
deeef d
eeef fddZded
efddZded
efddZdejd
dfddZded
dfddZ			d?dedededB deeged f dB ded
efd d!Zd"ejded
dfd#d$Zd%ed
dfd&d'Zd%ed
dfd(d)Z	d@dejdeded
dfd*d+Z	d@dejd%eded
dfd,d-Zd.eded
dfd/d0Z ded1ed
dfd2d3Z!d"ejd4e"ded5e#e$ d6eg df d7e%d
dfd8d9Z&d"ejd
dfd:d;Z'd
edB fd<d=Z(dS )AStreamableHTTPTransportz/StreamableHTTP client transport implementation.N   ,  urlr(   timeoutr-   authreturnc                 C   st   || _ |pi | _t|tr| n|| _t|tr| n|| _|| _d| _d| _	t
t dt tti| j| _dS )aS  Initialize the StreamableHTTP transport.

        Args:
            url: The endpoint URL.
            headers: Optional headers to include in requests.
            timeout: HTTP timeout for regular operations.
            sse_read_timeout: Timeout for SSE read operations.
            auth: Optional HTTPX authentication handler.
        Nz, )r8   r(   
isinstancer   total_secondsr9   r-   r:   r)   protocol_versionACCEPTJSONSSECONTENT_TYPErequest_headers)selfr8   r(   r9   r-   r:   r"   r"   r#   __init__M   s   

z StreamableHTTPTransport.__init__base_headersc                 C   s,   |  }| jr| j|t< | jr| j|t< |S )zAUpdate headers with session ID and protocol version if available.)copyr)   MCP_SESSION_IDr>   MCP_PROTOCOL_VERSION)rD   rF   r(   r"   r"   r#   _prepare_request_headersm   s   

z0StreamableHTTPTransport._prepare_request_headersmessagec                 C      t |jto|jjdkS )z2Check if the message is an initialization request.
initialize)r<   rootr   methodrD   rK   r"   r"   r#   _is_initialization_requestv      z2StreamableHTTPTransport._is_initialization_requestc                 C   rL   )z4Check if the message is an initialized notification.znotifications/initialized)r<   rN   r   rO   rP   r"   r"   r#   _is_initialized_notificationz   rR   z4StreamableHTTPTransport._is_initialized_notificationresponsec                 C   s0   |j t}|r|| _td| j  dS dS )z3Extract and store session ID from response headers.zReceived session ID: N)r(   getrH   r)   loggerinfo)rD   rT   new_session_idr"   r"   r#   '_maybe_extract_session_id_from_response~   s
   z?StreamableHTTPTransport._maybe_extract_session_id_from_responsec              
   C   s   t |jtrI|jjrKzt|jj}t|j| _t	
d| j  W dS  tyH } zt	d|  t	d|jj  W Y d}~dS d}~ww dS dS )z>Extract protocol version from initialization response message.zNegotiated protocol version: z=Failed to parse initialization response as InitializeResult: zRaw result: N)r<   rN   r   resultr   model_validater2   protocolVersionr>   rV   rW   	Exceptionwarning)rD   rK   init_resultexcr"   r"   r#   ,_maybe_extract_protocol_version_from_message   s   "zDStreamableHTTPTransport._maybe_extract_protocol_version_from_messageFsser,   original_request_idresumption_callbackis_initializationc           	   
      s   |j dkrnzGt|j}td|  |r| | |dur,t|jt	t
B r,||j_t|}||I dH  |jrE|rE||jI dH  t|jt	t
B W S  tym } ztd ||I dH  W Y d}~dS d}~ww td|j   dS )z@Handle an SSE event, returning True if the response is complete.rK   zSSE message: NzError parsing SSE messageFzUnknown SSE event: )eventr   model_validate_jsondatarV   debugra   r<   rN   r   r   idr   sendr]   	exceptionr^   )	rD   rb   r,   rc   rd   re   rK   r*   r`   r"   r"   r#   _handle_sse_event   s,   
	


z)StreamableHTTPTransport._handle_sse_eventr'   c              
      s   z[| j sW dS | | j}t|d| j|tj| j| jdd4 I dH ,}|j	
  td | 2 z3 dH W }| ||I dH  q26 W d  I dH  W dS 1 I dH sUw   Y  W dS  tyw } ztd|  W Y d}~dS d}~ww )z0Handle GET stream for server-initiated messages.NGETreadr(   r9   zGET SSE connection establishedzGET stream error (non-fatal): )r)   rJ   rC   r   r8   r.   Timeoutr9   r-   rT   raise_for_statusrV   ri   	aiter_sserm   r]   )rD   r'   r,   r(   event_sourcerb   r`   r"   r"   r#   handle_get_stream   s.   

2z)StreamableHTTPTransport.handle_get_streamctxc              	      s  |  |j}|jr|jjr|jj|t< ntdd}t|jjj	t
r)|jjj	j}t|jd| j|tj| j| jdd4 I dH A}|j  td | 2 z%3 dH W }| ||j||jra|jjndI dH }|rs|j I dH   nqN6 W d  I dH  dS 1 I dH sw   Y  dS )z/Handle a resumption request using GET with SSE.z.Resumption request requires a resumption tokenNrn   ro   rq   z)Resumption GET SSE connection established)rJ   r(   r+   resumption_tokenLAST_EVENT_IDr%   r<   r*   rK   rN   r   rj   r   r'   r8   r.   rr   r9   r-   rT   rs   rV   ri   rt   rm   r,   on_resumption_token_updateaclose)rD   rw   r(   rc   ru   rb   is_completer"   r"   r#   _handle_resumption_request   s>   


.z2StreamableHTTPTransport._handle_resumption_requestc              	      s  |  |j}|jj}| |}|jjd| j|jdddd|d4 I dH }|j	dkr<t
d 	 W d  I dH  dS |j	d	kr_t|jtrS| |j|jjI dH  	 W d  I dH  dS |  |rj| | t|jtr|jtd
 }|tr| ||j|I dH  n0|tr| |||I dH  n+| ||jI dH  W d  I dH  dS W d  I dH  dS W d  I dH  dS W d  I dH  dS 1 I dH sw   Y  dS )z/Handle a POST request with response processing.POSTTjson)by_aliasmodeexclude_none)r   r(   N   zReceived 202 Acceptedi   )rJ   r(   r*   rK   rQ   r'   streamr8   
model_dumpstatus_coderV   ri   r<   rN   r   _send_session_terminated_errorr,   rj   rs   rY   rU   rB   lower
startswithr@   _handle_json_responserA   _handle_sse_response_handle_unexpected_content_type)rD   rw   r(   rK   re   rT   content_typer"   r"   r#   _handle_post_request   sV   








.z,StreamableHTTPTransport._handle_post_requestc              
      s   z"|  I dH }t|}|r| | t|}||I dH  W dS  tyC } ztd ||I dH  W Y d}~dS d}~ww )z%Handle JSON response from the server.NzError parsing JSON response)	areadr   rg   ra   r   rk   r]   rV   rl   )rD   rT   r,   re   contentrK   r*   r`   r"   r"   r#   r   (  s   


z-StreamableHTTPTransport._handle_json_responsec              
      s   z4t |}| 2 z'3 dH W }| j||j|jr|jjnd|dI dH }|r1| I dH   W dS q
6 W dS  tyV } zt	d |j
|I dH  W Y d}~dS d}~ww )z$Handle SSE response from the server.N)rd   re   zError reading SSE stream:)r   rt   rm   r,   r+   rz   r{   r]   rV   rl   rk   )rD   rT   rw   re   ru   rb   r|   er"   r"   r#   r   =  s(   
 z,StreamableHTTPTransport._handle_sse_responser   c                    s.   d| }t | |t|I dH  dS )z+Handle unexpected content type in response.zUnexpected content type: N)rV   errorrk   
ValueError)rD   r   r,   	error_msgr"   r"   r#   r   V  s   

z7StreamableHTTPTransport._handle_unexpected_content_type
request_idc                    s8   t d|tdddd}tt|}||I dH  dS )z)Send a session terminated error response.z2.0iX  zSession terminated)coderK   )jsonrpcrj   r   N)r   r   r   r   rk   )rD   r,   r   jsonrpc_errorr*   r"   r"   r#   r   `  s   
z6StreamableHTTPTransport._send_session_terminated_errorwrite_stream_readerwrite_streamstart_get_streamtgc                    sn  zzu|4 I dH b |2 zS3 dH W }|j }t|jtr|jnd}	t|	o%|	jtd|  |r7|  t	|j
j||	|jd  fdd}
t|jtrY||
 q|
 I dH  q6 W d  I dH  n1 I dH sqw   Y  W n ty   td Y nw W | I dH  | I dH  dS W | I dH  | I dH  dS | I dH  | I dH  w )z&Handle writing requests to the server.NzSending client message: )r'   r(   r)   r*   r+   r,   r-   c                      s.   r  I d H  d S  I d H  d S N)r}   r   r"   rw   is_resumptionrD   r"   r#   handle_request_async  s   zAStreamableHTTPTransport.post_writer.<locals>.handle_request_asynczError in post_writer)rK   r<   r+   r   boolrx   rV   ri   rS   r&   rC   r)   r-   rN   r   
start_soonr]   rl   r{   )rD   r'   r   r,   r   r   r   r*   rK   r+   r   r"   r   r#   post_writern  sP   



((z#StreamableHTTPTransport.post_writerc              
      s   | j sdS z2| | j}|j| j|dI dH }|jdkr%td W dS |jdvr6td|j  W dS W dS  t	yS } ztd|  W Y d}~dS d}~ww )z2Terminate the session by sending a DELETE request.N)r(   i  z)Server does not allow session termination)      zSession termination failed: )
r)   rJ   rC   deleter8   r   rV   ri   r^   r]   )rD   r'   r(   rT   r`   r"   r"   r#   terminate_session  s   

z)StreamableHTTPTransport.terminate_sessionc                 C   s   | j S )zGet the current session ID.)r)   )rD   r"   r"   r#   get_session_id  s   z&StreamableHTTPTransport.get_session_id)Nr6   r7   N)NNF)F))r   r   r    r!   r2   r1   r4   r   r.   AuthrE   rJ   r   r   rQ   rS   ResponserY   ra   r   r3   r   r   r   rm   r/   rv   r&   r}   r   r   r   r   r   StreamReaderr
   r   r   r   r   r   r"   r"   r"   r#   r5   J   s    
" 	



)
".






9r5   r6   r7   Tr8   r(   r9   r-   terminate_on_closehttpx_client_factoryr:   r;   c                   s  t | ||||tjttB  d\}tjt d\}}	t 4 I dH ztd|   |jt	j
jjdjd4 I dH P d	 fdd}
j |	||
 z||jfV  W jrp|rp I dH  j  njr|r I dH  j  w W d  I dH  n1 I dH sw   Y  W  I dH  | I dH  n I dH  | I dH  w W d  I dH  dS 1 I dH sw   Y  dS )
a  
    Client transport for StreamableHTTP.

    `sse_read_timeout` determines how long (in seconds) the client will wait for a new
    event before disconnecting. All other HTTP operations are controlled by `timeout`.

    Yields:
        Tuple containing:
            - read_stream: Stream for reading messages from the server
            - write_stream: Stream for sending messages to the server
            - get_session_id_callback: Function to retrieve the current session ID
    r   Nz'Connecting to StreamableHTTP endpoint: ro   )r(   r9   r:   r;   c                      s    j  d S r   )r   rv   r"   r'   r,   r   	transportr"   r#   r     s   z/streamablehttp_client.<locals>.start_get_stream)r;   N)r5   anyiocreate_memory_object_streamr   r]   create_task_grouprV   ri   rC   r.   rr   r9   r-   r:   r   r   r   r)   r   cancel_scopecancelr{   )r8   r(   r9   r-   r   r   r:   read_streamr   r   r   r"   r   r#   streamablehttp_client  sR   


(.r   )@r!   loggingcollections.abcr   r   r   
contextlibr   dataclassesr   datetimer   r   r.   	anyio.abcr   anyio.streams.memoryr	   r
   	httpx_sser   r   r   mcp.shared._httpx_utilsr   r   mcp.shared.messager   r   	mcp.typesr   r   r   r   r   r   r   r   	getLoggerr   rV   r]   SessionMessageOrErrorr3   r   r2   GetSessionIdCallbackrH   rI   ry   rB   r?   r@   rA   r   r%   r&   r5   r1   r4   r   r   tupler   r"   r"   r"   r#   <module>   s    (
  t
