o
    iJ*                     @   s   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 ddl m!Z!m"Z" e#e$Z%G d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)TransportSecurityMiddlewareTransportSecuritySettings)ServerMessageMetadataSessionMessagec                       s   e Zd ZU dZeed< eeee	e
B  f ed< eed< ddededB ddf fd	d
ZedededefddZdedededdfddZ  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	_securityNendpointsecurity_settingsreturnc                    sx   t    d|v s|dsd|v sd|v rtd| d|ds'd| }|| _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   loggerdebug)selfr   r   	__class__ E/home/app/Keep/.python/lib/python3.10/site-packages/mcp/server/sse.pyr   P   s   
"


zSseServerTransport.__init__scopereceivesendc              	     s  |d dkrt d tdt||}| jj|ddI d H }|r/||||I d H  tdt d td	\}td	\}t	 | j
< t d
  |dd}|d| j }	t|	 dj  tjtttf  d	\ fddt 4 I d H 4}
dtdtdtffdd}t d |
|||| t d ||fV  W d   I d H  d S 1 I d H sw   Y  d S )Ntypehttpz%connect_sse received non-HTTP requestz)connect_sse can only handle HTTP requestsFZis_postzRequest validation failedzSetting up SSE connectionr   zCreated new session with ID: 	root_path r   z?session_id=c               
      s   t d 4 I d H ^ 4 I d H A d dI d H  t d   2 z3 d H W } t d|   d| jjddddI d H  q)6 W d   I d H  n1 I d H sZw   Y  W d   I d H  d S 1 I d H spw   Y  d S )	NzStarting SSE writerr   )eventdatazSent endpoint event: zSending message via SSE: messageT)Zby_aliasZexclude_none)r"   r#   r+   r3   Zmodel_dump_json)session_message)client_post_uri_datasse_stream_writerwrite_stream_readerr'   r(   
sse_writer   s   
Xz2SseServerTransport.connect_sse.<locals>.sse_writerr)   r*   r+   c                    sL   t d| ||I dH    I dH   I dH  td  dS )z
                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.
                )contentZdata_sender_callableNzClient session disconnected )r
   acloseloggingr#   )r)   r*   r+   )read_stream_writer
session_idsse_stream_readerr8   r7   r'   r(   response_wrapper   s   

z8SseServerTransport.connect_sse.<locals>.response_wrapperzStarting SSE response taskzYielding read and write streams)r"   errorr!   r   r   validate_requestr#   anyioZcreate_memory_object_streamr   r   getrstripr   r   hexdictstrr   Zcreate_task_groupr   r   r   Z
start_soon)r$   r)   r*   r+   requesterror_responseread_streamZwrite_streamr/   Zfull_message_path_for_clienttgr?   r'   )r5   r<   r=   r>   r6   r8   r7   r(   connect_ssey   s6   



"

.zSseServerTransport.connect_ssec              
      s  t d t||}| jj|ddI d H }|r!||||I d H S |jd}|d u r?t d tddd}||||I d H S zt	|d	}t d
|  W n  t
yn   t d|  tddd}||||I d H  Y S w | j|}	|	st d|  tddd}||||I d H S | I d H }
t d|
  ztj|
}t d|  W n/ ty } z#t d tddd}||||I d H  |	|I d H  W Y d }~d S d }~ww t|d}t||d}t d|  tddd}||||I d H  |	|I d H  d S )NzHandling POST messageTr.   r=   z#Received request without session_idzsession_id is requiredi  )status_code)rE   zParsed session ID: zReceived invalid session ID: zInvalid session IDzCould not find session for ID: zCould not find sessioni  z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   rA   Zquery_paramsrC   warningr   r   r!   r   bodytypesZJSONRPCMessageZmodel_validate_jsonr	   	exceptionr+   r   r   )r$   r)   r*   r+   rH   rI   Zsession_id_paramresponser=   writerrS   r3   errrO   r4   r'   r'   r(   handle_post_message   sV   





z&SseServerTransport.handle_post_message)N)__name__
__module____qualname____doc__rG   __annotations__rF   r   r   r   	Exceptionr   r   r   r   r   r   r   rL   rY   __classcell__r'   r'   r%   r(   r   @   s   
  )"Or   )'r]   r;   
contextlibr   typingr   urllib.parser   uuidr   r   rB   Zanyio.streams.memoryr   r   Zpydanticr	   Zsse_starletter
   Zstarlette.requestsr   Zstarlette.responsesr   Zstarlette.typesr   r   r   Z	mcp.typesrT   Zmcp.server.transport_securityr   r   Zmcp.shared.messager   r   	getLoggerrZ   r"   r   r'   r'   r'   r(   <module>   s$    '
