
    Rmj&0                        d 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
 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 ddlmZmZm Z  ddl!m"Z"m#Z# ddl$m%Z%m&Z&  ej'        e(          Z) G d d          Z*dS )a  
SSE Server Transport Module

This module implements a Server-Sent Events (SSE) transport layer for MCP servers.

Example usage:
```
    # Create an SSE transport at an endpoint
    sse = SseServerTransport("/messages/")

    # Create Starlette routes for SSE and message handling
    routes = [
        Route("/sse", endpoint=handle_sse, methods=["GET"]),
        Mount("/messages/", app=sse.handle_post_message),
    ]

    # Define handler functions
    async def handle_sse(request):
        async with sse.connect_sse(
            request.scope, request.receive, request._send
        ) as streams:
            await app.run(
                streams[0], streams[1], app.create_initialization_options()
            )
        # Return empty response to avoid NoneType error
        return Response()

    # Create and run Starlette app
    starlette_app = Starlette(routes=routes)
    uvicorn.run(starlette_app, host="127.0.0.1", port=port)
```

Note: The handle_sse function must return a Response to avoid a "TypeError: 'NoneType'
object is not callable" error when client disconnects. The example above returns
an empty Response() after the SSE connection ends to fix this.

See SseServerTransport class documentation for more details.
    N)asynccontextmanager)Any)quote)UUIDuuid4)MemoryObjectReceiveStreamMemoryObjectSendStream)ValidationError)EventSourceResponse)Request)Response)ReceiveScopeSend)AuthenticatedUserAuthorizationContextauthorization_context)TransportSecurityMiddlewareTransportSecuritySettings)ServerMessageMetadataSessionMessagec                        e Zd ZU dZeed<   eeee	e
z           f         ed<   eeef         ed<   eed<   ddededz  d	df fd
Zedededefd            Zdededed	dfdZ xZS )SseServerTransporta  
    SSE server transport for MCP. This class provides _two_ ASGI applications,
    suitable to be used with a framework like Starlette and a server like Hypercorn:

        1. connect_sse() is an ASGI application which receives incoming GET requests,
           and sets up a new SSE stream to send server messages to the client.
        2. handle_post_message() is an ASGI application which receives incoming POST
           requests, which should contain client messages that link to a
           previously-established SSE session.
    	_endpoint_read_stream_writers_session_owners	_securityNendpointsecurity_settingsreturnc                 p   t                                                       d|v s|                    d          sd|v sd|v rt          d| d          |                    d          sd|z   }|| _        i | _        i | _        t          |          | _        t          
                    d|            d	S )
a  
        Creates a new SSE server transport, which will direct the client to POST
        messages to the relative path given.

        Args:
            endpoint: A relative path where messages should be posted
                    (e.g., "/messages/").
            security_settings: Optional security settings for DNS rebinding protection.

        Note:
            We use relative paths instead of full URLs for several reasons:
            1. Security: Prevents cross-origin requests by ensuring clients only connect
               to the same origin they established the SSE connection with
            2. Flexibility: The server can be mounted at any path without needing to
               know its full URL
            3. Portability: The same endpoint configuration works across different
               environments (development, staging, production)

        Raises:
            ValueError: If the endpoint is a full URL instead of a relative path
        z://z//?#zGiven endpoint: z] is not a relative path (e.g., '/messages/'), expecting a relative path (e.g., '/messages/')./z.SseServerTransport initialized with endpoint: N)super__init__
startswith
ValueErrorr   r   r   r   r   loggerdebug)selfr   r   	__class__s      V/home/thesage/.hermes/hermes-agent/venv/lib/python3.11/site-packages/mcp/server/sse.pyr&   zSseServerTransport.__init__T   s    . 	 H 3 3D 9 9SH__PSW_P_P_B8 B B B   ""3'' 	&X~H!$&!!45FGGPhPPQQQQQ    scopereceivesendc                  K   |d         dk    r)t                               d           t          d          t          ||          }| j                            |d           d {V }|r" ||||           d {V  t          d          t                               d           t          j        d	          \  }t          j        d	          \  }t                      |
                    d
          }t          |t                    rt          |          | j        <   | j        <   t                               d            |
                    dd          }	|	                    d          | j        z   }
t%          |
           dj         t          j        t(          t*          t,          f                  d	          \  fd	 t          j                    4 d {V }dt0          dt2          dt4          ffd}t                               d           |                    ||||           t                               d           ||fW V  d d d           d {V  n# 1 d {V swxY w Y   | j                            d            | j                            d            d S # | j                            d            | j                            d            w xY w)Ntypehttpz%connect_sse received non-HTTP requestz)connect_sse can only handle HTTP requestsFis_postzRequest validation failedzSetting up SSE connectionr   userzCreated new session with ID: 	root_path r$   z?session_id=c            
      *  K   t                               d           4 d {V  4 d {V                      dd           d {V  t                               d            2 3 d {V } t                               d|                                 d| j                            dd          d           d {V  ]6 	 d d d           d {V  n# 1 d {V swxY w Y   d d d           d {V  d S # 1 d {V swxY w Y   d S )	NzStarting SSE writerr   )eventdatazSent endpoint event: zSending message via SSE: messageT)by_aliasexclude_none)r)   r*   r1   r=   model_dump_json)session_messageclient_post_uri_datasse_stream_writerwrite_stream_readers    r-   
sse_writerz2SseServerTransport.connect_sse.<locals>.sse_writer   s     LL.///(        *=        ',,zK_-`-`aaaaaaaaaK5IKKLLL-@       /LL!N_!N!NOOO+00%.$3$;$K$KUYhl$K$m$m           .A-@	                                                        sA   	D>C-C
3ACD
C(	(D+C(	,D
DDr/   r0   r1   c                   K    t                    | ||           d{V                                   d{V                                   d{V                                   d{V  t          j        d            dS )a   
                    The EventSourceResponse returning signals a client close / disconnect.
                    In this case we close our side of the streams to signal the client that
                    the connection has been closed.
                    )contentdata_sender_callableNzClient session disconnected )r   acloseloggingr*   )r/   r0   r1   read_stream_writer
session_idsse_stream_readerrE   rD   s      r-   response_wrapperz8SseServerTransport.connect_sse.<locals>.response_wrapper   s       j-6G^hiiiw         -33555555555-44666666666+22444444444M"M"M"MNNNNNr.   zStarting SSE response taskzYielding read and write streams)r)   errorr(   r   r   validate_requestr*   anyiocreate_memory_object_streamr   get
isinstancer   r   r   r   rstripr   r   hexdictstrr   create_task_groupr   r   r   
start_soonpop)r+   r/   r0   r1   requesterror_responseread_streamwrite_streamr7   r8   full_message_path_for_clienttgrN   rB   rK   rL   rM   rC   rE   rD   s                @@@@@@@r-   connect_ssezSseServerTransport.connect_sse~   s     =F""LL@AAAHIII %))#~>>wPU>VVVVVVVV 	: .66666666689990111 +0*KA*N*N'K,1,Ma,P,P))WW
yy  d-.. 	K/DT/J/JD ,0B!*-AZAABBB IIk2..	 (1'7'7'<'<t~'M$ #((D"E"EccS]Sacc/4/PQUVY[^V^Q_/`ab/c/c,,	 	 	 	 	 	 		7.00 2 2 2 2 2 2 2BO% O' OQU O O O O O O O O O O 9:::.wEEE>???"L11111)2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 22 %))*d;;; $$Z66666 %))*d;;; $$Z6666s1   K 4A3I9'K 9
JK JK 8K:c                 0  K   t                               d           t          ||          }| j                            |d           d {V }|r ||||           d {V S |j                            d          }|>t                               d           t          dd          } ||||           d {V S 	 t          |	          }t                               d
|            nQ# t          $ rD t                               d|            t          dd          } ||||           d {V cY S w xY w| j                            |          }	|	sAt                               d|            t          dd          } ||||           d {V S |                    d          }
t          |
t                    rt          |
          nd }|| j                            |          k    r?t                               d|           t          dd          } ||||           d {V S |                                 d {V }t                               d|            	 t"          j                            |          }t                               d|            nq# t(          $ rd}t                               d           t          dd          } ||||           d {V  |	                    |           d {V  Y d }~d S d }~ww xY wt/          |          }t1          ||          }t                               d|            t          dd          } ||||           d {V  |	                    |           d {V  d S )NzHandling POST messageTr5   rL   z#Received request without session_idzsession_id is requiredi  )status_code)rV   zParsed session ID: zReceived invalid session ID: zInvalid session IDzCould not find session for ID: zCould not find sessioni  r7   z;Rejecting message for session %s: credential does not matchzReceived JSON: zValidated client message: zFailed to parse messagezCould not parse message)request_context)metadataz#Sending session message to writer: Accepted   )r)   r*   r   r   rP   query_paramsrS   warningr   r   r(   r   rT   r   r   r   bodytypesJSONRPCMessagemodel_validate_jsonr
   	exceptionr1   r   r   )r+   r/   r0   r1   r\   r]   session_id_paramresponserL   writerr7   	requestorrk   r=   errrf   rA   s                    r-   handle_post_messagez&SseServerTransport.handle_post_message   s`     ,---%))  $~>>wPT>UUUUUUUU 	>'w========="/33LAA#NN@AAA 8cJJJH!%$777777777	8"2333JLL;z;;<<<< 	8 	8 	8NNM;KMMNNN 4#FFFH!%$77777777777	8
 *..z:: 	8NNIZIIJJJ 8cJJJH!%$777777777yy  3=dDU3V3V`)$///\`	,00<<<< NNXZdeee 8cJJJH!%$777777777\\^^######-t--...	*>>tDDGLL?g??@@@@ 	 	 	6777 9sKKKH(5'4000000000++c"""""""""FFFFF	 )AAA(8DDDL?LLMMMJC888hugt,,,,,,,,,kk/***********s,   ?-C- -AD;:D;+<J( (
L2ALL)N)__name__
__module____qualname____doc__rX   __annotations__rW   r   r	   r   	Exceptionr   r   r   r&   r   r   r   r   rb   ru   __classcell__)r,   s   @r-   r   r   A   s6        	 	 NNNt%;NY<V%WWXXXX $ 445555****(R (R (R9RUY9Y (Rei (R (R (R (R (R (RT X7u X7w X7d X7 X7 X7 X7t9+u 9+w 9+d 9+W[ 9+ 9+ 9+ 9+ 9+ 9+ 9+ 9+r.   r   )+ry   rJ   
contextlibr   typingr   urllib.parser   uuidr   r   rQ   anyio.streams.memoryr   r	   pydanticr
   sse_starletter   starlette.requestsr   starlette.responsesr   starlette.typesr   r   r   	mcp.typesrl   &mcp.server.auth.middleware.bearer_authr   r   r   mcp.server.transport_securityr   r   mcp.shared.messager   r   	getLoggerrv   r)   r    r.   r-   <module>r      s  % %N  * * * * * *                      R R R R R R R R $ $ $ $ $ $ - - - - - - & & & & & & ( ( ( ( ( ( 0 0 0 0 0 0 0 0 0 0       q q q q q q q q q q        E D D D D D D D		8	$	$Q+ Q+ Q+ Q+ Q+ Q+ Q+ Q+ Q+ Q+r.   