4646from mcp .shared ._stream_protocols import ReadStream , WriteStream
4747from mcp .shared .inbound import MCP_PROTOCOL_VERSION_HEADER
4848from mcp .shared .message import CloseSSEStreamCallback , ServerMessageMetadata , SessionMessage
49+ from mcp .shared .wire import serialize_jsonrpc_message , serialize_outbound_message
4950
5051logger = logging .getLogger (__name__ )
5152
@@ -366,7 +367,10 @@ async def _run_sse_writer(
366367 if priming_event is not None :
367368 await sse_stream_writer .send (priming_event )
368369 async for event_message in request_stream_reader :
369- await sse_stream_writer .send (self ._create_event_data (event_message ))
370+ event_data = self ._create_event_data (event_message )
371+ if event_data is None :
372+ continue
373+ await sse_stream_writer .send (event_data )
370374 if isinstance (event_message .message , JSONRPCResponse | JSONRPCError ):
371375 break
372376 except anyio .ClosedResourceError : # pragma: lax no cover
@@ -401,7 +405,7 @@ def _create_error_response(
401405 )
402406
403407 return Response (
404- error_response . model_dump_json ( by_alias = True , exclude_unset = True ),
408+ serialize_jsonrpc_message ( error_response ),
405409 status_code = status_code ,
406410 headers = response_headers ,
407411 )
@@ -420,8 +424,21 @@ def _create_json_response(
420424 if self .mcp_session_id :
421425 response_headers [MCP_SESSION_ID_HEADER ] = self .mcp_session_id
422426
427+ body = None
428+ if response_message is not None :
429+ body = serialize_outbound_message (response_message )
430+ if body is None :
431+ # Request/notification with no id fallback: still answer the HTTP
432+ # request so the session is not left without a body.
433+ body = serialize_jsonrpc_message (
434+ JSONRPCError (
435+ jsonrpc = "2.0" ,
436+ id = None ,
437+ error = ErrorData (code = INTERNAL_ERROR , message = "Internal server error" ),
438+ )
439+ )
423440 return Response (
424- response_message . model_dump_json ( by_alias = True , exclude_unset = True ) if response_message else None ,
441+ body ,
425442 status_code = status_code ,
426443 headers = response_headers ,
427444 )
@@ -430,11 +447,19 @@ def _get_session_id(self, request: Request) -> str | None:
430447 """Extract the session ID from request headers."""
431448 return request .headers .get (MCP_SESSION_ID_HEADER )
432449
433- def _create_event_data (self , event_message : EventMessage ) -> SSEEvent :
434- """Create event data dictionary from an EventMessage."""
435- event_data = {
450+ def _create_event_data (self , event_message : EventMessage ) -> SSEEvent | None :
451+ """Create event data dictionary from an EventMessage.
452+
453+ Returns None when the message cannot be serialized and has no request id
454+ to attach an Internal server error to, so the SSE loop can skip it.
455+ """
456+ data = serialize_outbound_message (event_message .message )
457+ if data is None :
458+ return None
459+
460+ event_data : SSEEvent = {
436461 "event" : "message" ,
437- "data" : event_message . message . model_dump_json ( by_alias = True , exclude_unset = True ) ,
462+ "data" : data ,
438463 }
439464
440465 # If an event ID was provided, include it
@@ -795,6 +820,8 @@ async def standalone_sse_writer():
795820
796821 # Send the message via SSE
797822 event_data = self ._create_event_data (event_message )
823+ if event_data is None :
824+ continue
798825 await sse_stream_writer .send (event_data )
799826 except anyio .ClosedResourceError :
800827 # Session teardown can close the stream while the writer is between dequeues.
@@ -958,6 +985,8 @@ async def replay_sender():
958985 # Define an async callback for sending events
959986 async def send_event (event_message : EventMessage ) -> None :
960987 event_data = self ._create_event_data (event_message )
988+ if event_data is None :
989+ return
961990 await sse_stream_writer .send (event_data )
962991
963992 # Replay past events and get the stream ID
@@ -986,7 +1015,8 @@ async def send_event(event_message: EventMessage) -> None:
9861015 async with msg_reader :
9871016 async for event_message in msg_reader :
9881017 event_data = self ._create_event_data (event_message )
989-
1018+ if event_data is None :
1019+ continue
9901020 await sse_stream_writer .send (event_data )
9911021 finally :
9921022 self ._sse_stream_writers .pop (stream_id , None )
0 commit comments