
    Ch"*                        S r SSKrSSKJr  SSKJr  SSKJr  SSKJ	r	J
r
  SSKrSSKJrJr  SSKJr  SS	KJr  SS
KJr  SSKJr  SSKJrJrJr  SSKJr  SSKJrJr  SSK J!r!J"r"  \RF                  " \$5      r% " S S5      r&g)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                      ^  \ rS rSr% Sr\\S'   \\\	\
\-     4   \S'   \\S'   SS\S\S-  S	S4U 4S
 jjjr\S\S\S\4S j5       rS\S\S\S	S4S jrSrU =r$ )SseServerTransport@   a  
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                 0  > [         TU ]  5         SU;   d"  UR                  S5      (       d  SU;   d  SU;   a  [        SU S35      eUR                  S5      (       d  SU-   nXl        0 U l        [        U5      U l        [        R                  SU 35        g	)
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__s      H/home/james-whalen/.local/lib/python3.13/site-packages/mcp/server/sse.pyr#   SseServerTransport.__init__P   s    . 	 H 3 3D 9 9SH_PSW_P_"8* -B B  ""3''X~H!$&!45FGEhZPQ    scopereceivesendc                ^  ^^^^^^^#    US   S:w  a   [         R                  S5        [        S5      e[        X5      nU R                  R                  USS9I S h  vN nU(       a  U" XU5      I S h  vN   [        S5      e[         R                  S5        [        R                  " S	5      u  mn[        R                  " S	5      u  nm[        5       mTU R                  T'   [         R                  S
T 35        UR                  SS5      nUR                  S5      U R                  -   n	[        U	5       STR                   3m[        R                  [         ["        [$        4      " S	5      u  mmUUU4S jm[        R&                  " 5        IS h  vN n
S[(        S[*        S[,        4UUUUU4S jjn[         R                  S5        U
R/                  XX#5        [         R                  S5        Xg47v   S S S 5      IS h  vN   g  GN GN N{ N! , IS h  vN  (       d  f       g = f7f)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   zCreated new session with ID: 	root_path r!   z?session_id=c                  B  >#    [         R                  S5        T IS h  vN   T IS h  vN   TR                  STS.5      I S h  vN   [         R                  ST 35        T  S h  vN n [         R                  SU  35        TR                  SU R                  R	                  SSS9S.5      I S h  vN   MW   N N Ny NX N
 S S S 5      IS h  vN    O! , IS h  vN  (       d  f       O= fS S S 5      IS h  vN    g ! , IS h  vN  (       d  f       g = f7f)	NzStarting SSE writerr   )eventdatazSent endpoint event: zSending message via SSE: messageT)by_aliasexclude_none)r&   r'   r/   r:   model_dump_json)session_messageclient_post_uri_datasse_stream_writerwrite_stream_readers    r*   
sse_writer2SseServerTransport.connect_sse.<locals>.sse_writer   s     LL./((*=*=',,zK_-`aaa45I4JKL-@ /LL#<_<M!NO+00%.$3$;$;$K$KUYhl$K$m   )*=a .A	 +>*=*=*=*=*=((((((s   DB<DDB>DCC C%C)C
*C-AC5C
6C<D>D CCCCDCDC0	C" C0	,D3D>D?DDDDDr-   r.   r/   c                    >#    [        TTS9" XU5      I Sh  vN   TR                  5       I Sh  vN   TR                  5       I Sh  vN   [        R                  " ST 35        g NN N8 N"7f)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.
)contentdata_sender_callableNzClient session disconnected )r   acloseloggingr'   )r-   r.   r/   read_stream_writer
session_idsse_stream_readerrB   rA   s      r*   response_wrapper8SseServerTransport.connect_sse.<locals>.response_wrapper   so      *2CZdeD   )//111)00222 <ZLIJ 22s1   A-A'A-A)A-A+	A-)A-+A-zStarting SSE response taskzYielding read and write streams)r&   errorr%   r   r   validate_requestr'   anyiocreate_memory_object_streamr   r   getrstripr   r   hexdictstrr   create_task_groupr   r   r   
start_soon)r(   r-   r.   r/   requesterror_responseread_streamwrite_streamr5   full_message_path_for_clienttgrL   r?   rI   rJ   rK   r@   rB   rA   s               @@@@@@@r*   connect_sseSseServerTransport.connect_ssey   s    =F"LL@AHII %)#~~>>wPU>VV 66689901 +0*K*KA*N'K,1,M,Ma,P))W
0B!!*-4ZLAB IIk2.	 (1'7'7'<t~~'M$ #((D"E!FlS]SaSaRbc/4/P/PQUVY[^V^Q_/`ab/c,,	 **,,Ke Kg KT K K LL56MM*7ALL:;--' -,,g W6b -,,,sn   AH-H	H-3H4DH-HH-A!H8H-HH-H-H-H-H*HH*&H-c                 H  #    [         R                  S5        [        X5      nU R                  R	                  USS9I S h  vN nU(       a  U" XU5      I S h  vN $ UR
                  R                  S5      nUc0  [         R                  S5        [        SSS9nU" XU5      I S h  vN $  [        US	9n[         R                  S
U 35        U R                  R                  U5      n	U	(       d3  [         R                  SU 35        [        SSS9nU" XU5      I S h  vN $ UR                  5       I S h  vN n
[         R                  SU
 35         [        R                  R                  U
5      n[         R                  SU 35        ['        US9n[)        XS9n[         R                  SU 35        [        SSS9nU" XU5      I S h  vN   U	R%                  U5      I S h  vN   g  GN GN GNI! [         a7    [         R                  SU 35        [        SSS9nU" XU5      I S h  vN  s $ f = f GN GN! [          aU  n[         R#                  S5        [        SSS9nU" XU5      I S h  vN    U	R%                  U5      I S h  vN     S nAg S nAff = f N N7f)NzHandling POST messageTr3   rJ   z#Received request without session_idzsession_id is requiredi  )status_code)rT   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   rO   query_paramsrR   warningr   r   r%   r   bodytypesJSONRPCMessagemodel_validate_jsonr
   	exceptionr/   r   r   )r(   r-   r.   r/   rY   rZ   session_id_paramresponserJ   writerri   r:   errrd   r>   s                  r*   handle_post_message&SseServerTransport.handle_post_message   sg    ,-%)  $~~>>wPT>UU'==="//33LA#NN@A 8cJH!%$777	8"23JLL.zl;< **..z:NN<ZLIJ 8cJH!%$777\\^#tf-.	**>>tDGLL5gY?@ )A(D:?:KLMJC8ut,,,kk/***W V= 8
  	8NN:;K:LMN 4#FH!%$7777	8 8#  	67 9sKH54000++c"""	 	-*s   >J" G)J"G,AJ"&G/'J",!G2 AJ"H6J"5H96J"7H< 
A J"
JJ"#J $J",J"/J"28H3*H-+H30J"2H33J"9J"<
J+J1I42JJJJ"JJ" J")r   r   r   )N)__name__
__module____qualname____firstlineno____doc__rV   __annotations__rU   r   r	   r   	Exceptionr   r   r#   r   r   r   r   r_   rr   __static_attributes____classcell__)r)   s   @r*   r   r   @   s    	 Nt%;NY<V%WWXX**'R 'R9RUY9Y 'Rei 'R 'RR M.u M.w M.d M. M.^0+u 0+w 0+d 0+W[ 0+ 0+r,   r   )'rx   rH   
contextlibr   typingr   urllib.parser   uuidr   r   rP   anyio.streams.memoryr   r	   pydanticr
   sse_starletter   starlette.requestsr   starlette.responsesr   starlette.typesr   r   r   	mcp.typesrj   mcp.server.transport_securityr   r   mcp.shared.messager   r   	getLoggerrt   r&   r    r,   r*   <module>r      s^   %N  *     R $ - & ( 0 0  E			8	$y+ y+r,   