Use 120 characters instead of 88 (#856)

This commit is contained in:
Marcelo Trylesinski 2025-06-11 02:45:50 -07:00 • committed by GitHub
commit 543961968c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -82,9 +82,7 @@ class EventStore(ABC):
"""
@abstractmethod
async def store_event(
self, stream_id: StreamId, message: JSONRPCMessage
) -> EventId:
async def store_event(self, stream_id: StreamId, message: JSONRPCMessage) -> EventId:
"""
Stores an event for later retrieval.
@ -125,9 +123,7 @@ class StreamableHTTPServerTransport:
"""
# Server notification streams for POST requests as well as standalone SSE stream
_read_stream_writer: MemoryObjectSendStream[SessionMessage | Exception] | None = (
None
)
_read_stream_writer: MemoryObjectSendStream[SessionMessage | Exception] | None = None
_read_stream: MemoryObjectReceiveStream[SessionMessage | Exception] | None = None
_write_stream: MemoryObjectSendStream[SessionMessage] | None = None
_write_stream_reader: MemoryObjectReceiveStream[SessionMessage] | None = None
@ -153,12 +149,8 @@ class StreamableHTTPServerTransport:
Raises:
ValueError: If the session ID contains invalid characters.
"""
if mcp_session_id is not None and not SESSION_ID_PATTERN.fullmatch(
mcp_session_id
):
raise ValueError(
"Session ID must only contain visible ASCII characters (0x21-0x7E)"
)
if mcp_session_id is not None and not SESSION_ID_PATTERN.fullmatch(mcp_session_id):
raise ValueError("Session ID must only contain visible ASCII characters (0x21-0x7E)")
self.mcp_session_id = mcp_session_id
self.is_json_response_enabled = is_json_response_enabled
@ -218,9 +210,7 @@ class StreamableHTTPServerTransport:
response_headers[MCP_SESSION_ID_HEADER] = self.mcp_session_id
return Response(
response_message.model_dump_json(by_alias=True, exclude_none=True)
if response_message
else None,
response_message.model_dump_json(by_alias=True, exclude_none=True) if response_message else None,
status_code=status_code,
headers=response_headers,
)
@ -233,9 +223,7 @@ class StreamableHTTPServerTransport:
"""Create event data dictionary from an EventMessage."""
event_data = {
"event": "message",
"data": event_message.message.model_dump_json(
by_alias=True, exclude_none=True
),
"data": event_message.message.model_dump_json(by_alias=True, exclude_none=True),
}
# If an event ID was provided, include it
@ -283,42 +271,29 @@ class StreamableHTTPServerTransport:
accept_header = request.headers.get("accept", "")
accept_types = [media_type.strip() for media_type in accept_header.split(",")]
has_json = any(
media_type.startswith(CONTENT_TYPE_JSON) for media_type in accept_types
)
has_sse = any(
media_type.startswith(CONTENT_TYPE_SSE) for media_type in accept_types
)
has_json = any(media_type.startswith(CONTENT_TYPE_JSON) for media_type in accept_types)
has_sse = any(media_type.startswith(CONTENT_TYPE_SSE) for media_type in accept_types)
return has_json, has_sse
def _check_content_type(self, request: Request) -> bool:
"""Check if the request has the correct Content-Type."""
content_type = request.headers.get("content-type", "")
content_type_parts = [
part.strip() for part in content_type.split(";")[0].split(",")
]
content_type_parts = [part.strip() for part in content_type.split(";")[0].split(",")]
return any(part == CONTENT_TYPE_JSON for part in content_type_parts)
async def _handle_post_request(
self, scope: Scope, request: Request, receive: Receive, send: Send
) -> None:
async def _handle_post_request(self, scope: Scope, request: Request, receive: Receive, send: Send) -> None:
"""Handle POST requests containing JSON-RPC messages."""
writer = self._read_stream_writer
if writer is None:
raise ValueError(
"No read stream writer available. Ensure connect() is called first."
)
raise ValueError("No read stream writer available. Ensure connect() is called first.")
try:
# Check Accept headers
has_json, has_sse = self._check_accept_headers(request)
if not (has_json and has_sse):
response = self._create_error_response(
(
"Not Acceptable: Client must accept both application/json and "
"text/event-stream"
),
("Not Acceptable: Client must accept both application/json and " "text/event-stream"),
HTTPStatus.NOT_ACCEPTABLE,
)
await response(scope, receive, send)
@ -346,9 +321,7 @@ class StreamableHTTPServerTransport:
try:
raw_message = json.loads(body)
except json.JSONDecodeError as e:
response = self._create_error_response(
f"Parse error: {str(e)}", HTTPStatus.BAD_REQUEST, PARSE_ERROR
)
response = self._create_error_response(f"Parse error: {str(e)}", HTTPStatus.BAD_REQUEST, PARSE_ERROR)
await response(scope, receive, send)
return
@ -364,10 +337,7 @@ class StreamableHTTPServerTransport:
return
# Check if this is an initialization request
is_initialization_request = (
isinstance(message.root, JSONRPCRequest)
and message.root.method == "initialize"
)
is_initialization_request = isinstance(message.root, JSONRPCRequest) and message.root.method == "initialize"
if is_initialization_request:
# Check if the server already has an established session
@ -406,9 +376,7 @@ class StreamableHTTPServerTransport:
# Extract the request ID outside the try block for proper scope
request_id = str(message.root.id)
# Register this stream for the request ID
self._request_streams[request_id] = anyio.create_memory_object_stream[
EventMessage
](0)
self._request_streams[request_id] = anyio.create_memory_object_stream[EventMessage](0)
request_stream_reader = self._request_streams[request_id][1]
if self.is_json_response_enabled:
@ -424,16 +392,12 @@ class StreamableHTTPServerTransport:
# Use similar approach to SSE writer for consistency
async for event_message in request_stream_reader:
# If it's a response, this is what we're waiting for
if isinstance(
event_message.message.root, JSONRPCResponse | JSONRPCError
):
if isinstance(event_message.message.root, JSONRPCResponse | JSONRPCError):
response_message = event_message.message
break
# For notifications and request, keep waiting
else:
logger.debug(
f"received: {event_message.message.root.method}"
)
logger.debug(f"received: {event_message.message.root.method}")
# At this point we should have a response
if response_message:
@ -442,9 +406,7 @@ class StreamableHTTPServerTransport:
await response(scope, receive, send)
else:
# This shouldn't happen in normal operation
logger.error(
"No response message received before stream closed"
)
logger.error("No response message received before stream closed")
response = self._create_error_response(
"Error processing request: No response received",
HTTPStatus.INTERNAL_SERVER_ERROR,
@ -462,9 +424,7 @@ class StreamableHTTPServerTransport:
await self._clean_up_memory_streams(request_id)
else:
# Create SSE stream
sse_stream_writer, sse_stream_reader = (
anyio.create_memory_object_stream[dict[str, str]](0)
)
sse_stream_writer, sse_stream_reader = anyio.create_memory_object_stream[dict[str, str]](0)
async def sse_writer():
# Get the request ID from the incoming request message
@ -495,11 +455,7 @@ class StreamableHTTPServerTransport:
"Cache-Control": "no-cache, no-transform",
"Connection": "keep-alive",
"Content-Type": CONTENT_TYPE_SSE,
**(
{MCP_SESSION_ID_HEADER: self.mcp_session_id}
if self.mcp_session_id
else {}
),
**({MCP_SESSION_ID_HEADER: self.mcp_session_id} if self.mcp_session_id else {}),
}
response = EventSourceResponse(
content=sse_stream_reader,
@ -544,9 +500,7 @@ class StreamableHTTPServerTransport:
"""
writer = self._read_stream_writer
if writer is None:
raise ValueError(
"No read stream writer available. Ensure connect() is called first."
)
raise ValueError("No read stream writer available. Ensure connect() is called first.")
# Validate Accept header - must include text/event-stream
_, has_sse = self._check_accept_headers(request)
@ -585,17 +539,13 @@ class StreamableHTTPServerTransport:
return
# Create SSE stream
sse_stream_writer, sse_stream_reader = anyio.create_memory_object_stream[
dict[str, str]
](0)
sse_stream_writer, sse_stream_reader = anyio.create_memory_object_stream[dict[str, str]](0)
async def standalone_sse_writer():
try:
# Create a standalone message stream for server-initiated messages
self._request_streams[GET_STREAM_KEY] = (
anyio.create_memory_object_stream[EventMessage](0)
)
self._request_streams[GET_STREAM_KEY] = anyio.create_memory_object_stream[EventMessage](0)
standalone_stream_reader = self._request_streams[GET_STREAM_KEY][1]
async with sse_stream_writer, standalone_stream_reader:
@ -732,9 +682,7 @@ class StreamableHTTPServerTransport:
return True
async def _replay_events(
self, last_event_id: str, request: Request, send: Send
) -> None:
async def _replay_events(self, last_event_id: str, request: Request, send: Send) -> None:
"""
Replays events that would have been sent after the specified event ID.
Only used when resumability is enabled.
@ -754,9 +702,7 @@ class StreamableHTTPServerTransport:
headers[MCP_SESSION_ID_HEADER] = self.mcp_session_id
# Create SSE stream for replay
sse_stream_writer, sse_stream_reader = anyio.create_memory_object_stream[
dict[str, str]
](0)
sse_stream_writer, sse_stream_reader = anyio.create_memory_object_stream[dict[str, str]](0)
async def replay_sender():
try:
@ -767,15 +713,11 @@ class StreamableHTTPServerTransport:
await sse_stream_writer.send(event_data)
# Replay past events and get the stream ID
stream_id = await event_store.replay_events_after(
last_event_id, send_event
)
stream_id = await event_store.replay_events_after(last_event_id, send_event)
# If stream ID not in mapping, create it
if stream_id and stream_id not in self._request_streams:
self._request_streams[stream_id] = (
anyio.create_memory_object_stream[EventMessage](0)
)
self._request_streams[stream_id] = anyio.create_memory_object_stream[EventMessage](0)
msg_reader = self._request_streams[stream_id][1]
# Forward messages to SSE
@ -829,12 +771,8 @@ class StreamableHTTPServerTransport:
# Create the memory streams for this connection
read_stream_writer, read_stream = anyio.create_memory_object_stream[
SessionMessage | Exception
](0)
write_stream, write_stream_reader = anyio.create_memory_object_stream[
SessionMessage
](0)
read_stream_writer, read_stream = anyio.create_memory_object_stream[SessionMessage | Exception](0)
write_stream, write_stream_reader = anyio.create_memory_object_stream[SessionMessage](0)
# Store the streams
self._read_stream_writer = read_stream_writer
@ -867,35 +805,24 @@ class StreamableHTTPServerTransport:
session_message.metadata,
ServerMessageMetadata,
)
and session_message.metadata.related_request_id
is not None
and session_message.metadata.related_request_id is not None
):
target_request_id = str(
session_message.metadata.related_request_id
)
target_request_id = str(session_message.metadata.related_request_id)
request_stream_id = (
target_request_id
if target_request_id is not None
else GET_STREAM_KEY
)
request_stream_id = target_request_id if target_request_id is not None else GET_STREAM_KEY
# Store the event if we have an event store,
# regardless of whether a client is connected
# messages will be replayed on the re-connect
event_id = None
if self._event_store:
event_id = await self._event_store.store_event(
request_stream_id, message
)
event_id = await self._event_store.store_event(request_stream_id, message)
logger.debug(f"Stored {event_id} from {request_stream_id}")
if request_stream_id in self._request_streams:
try:
# Send both the message and the event ID
await self._request_streams[request_stream_id][0].send(
EventMessage(message, event_id)
)
await self._request_streams[request_stream_id][0].send(EventMessage(message, event_id))
except (
anyio.BrokenResourceError,
anyio.ClosedResourceError,