From 2f365d81d929d63d20ba786c8b1becd3df36fc13 Mon Sep 17 00:00:00 2001 From: CriticalGameEror <83310221+CriticalGameEror@users.noreply.github.com> Date: Wed, 23 Jul 2025 22:51:10 +0100 Subject: [PATCH 1/5] fix: ensures MCP client can handle 4xx error codes Ensures that any error code response from the MCP server is handled. --- src/mcp/client/streamable_http.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mcp/client/streamable_http.py b/src/mcp/client/streamable_http.py index 63b09133f..c8331829e 100644 --- a/src/mcp/client/streamable_http.py +++ b/src/mcp/client/streamable_http.py @@ -266,7 +266,8 @@ async def _handle_post_request(self, ctx: RequestContext) -> None: logger.debug("Received 202 Accepted") return - if response.status_code == 404: + # ensures that all HTTP error codes are handled + if response.status_code >= 400 and response.status_code <= 499: if isinstance(message.root, JSONRPCRequest): await self._send_session_terminated_error( ctx.read_stream_writer, From c14346497f50dc40ad9be9a11295baf4e17cfa72 Mon Sep 17 00:00:00 2001 From: CriticalGameEror <83310221+CriticalGameEror@users.noreply.github.com> Date: Wed, 23 Jul 2025 23:04:44 +0100 Subject: [PATCH 2/5] refactor: added a custom error message This ensures the error message is more specific and focused to a 400 bad request error only --- src/mcp/client/streamable_http.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/mcp/client/streamable_http.py b/src/mcp/client/streamable_http.py index c8331829e..de2d66714 100644 --- a/src/mcp/client/streamable_http.py +++ b/src/mcp/client/streamable_http.py @@ -266,8 +266,15 @@ async def _handle_post_request(self, ctx: RequestContext) -> None: logger.debug("Received 202 Accepted") return - # ensures that all HTTP error codes are handled - if response.status_code >= 400 and response.status_code <= 499: + if response.status_code == 400: + if isinstance(message.root, JSONRPCRequest): + await self._send_bad_request_error( + ctx.read_stream_writer, + message.root.id, + ) + return + + if response.status_code == 404: if isinstance(message.root, JSONRPCRequest): await self._send_session_terminated_error( ctx.read_stream_writer, @@ -360,6 +367,20 @@ async def _send_session_terminated_error( session_message = SessionMessage(JSONRPCMessage(jsonrpc_error)) await read_stream_writer.send(session_message) + async def _send_bad_request_error( + self, + read_stream_writer: StreamWriter, + request_id: RequestId, + ) -> None: + """Send a bad request error response.""" + jsonrpc_error = JSONRPCError( + jsonrpc="2.0", + id=request_id, + error=ErrorData(code=32600, message="Bad request"), + ) + session_message = SessionMessage(JSONRPCMessage(jsonrpc_error)) + await read_stream_writer.send(session_message) + async def post_writer( self, client: httpx.AsyncClient, From 3a45353aaeb22add9a409b2ae2819884c12a4627 Mon Sep 17 00:00:00 2001 From: CriticalGameEror <83310221+CriticalGameEror@users.noreply.github.com> Date: Wed, 23 Jul 2025 23:22:52 +0100 Subject: [PATCH 3/5] refactor: minor linting adjustments --- src/mcp/client/streamable_http.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mcp/client/streamable_http.py b/src/mcp/client/streamable_http.py index de2d66714..ce4be9a9f 100644 --- a/src/mcp/client/streamable_http.py +++ b/src/mcp/client/streamable_http.py @@ -273,7 +273,7 @@ async def _handle_post_request(self, ctx: RequestContext) -> None: message.root.id, ) return - + if response.status_code == 404: if isinstance(message.root, JSONRPCRequest): await self._send_session_terminated_error( From c9c3cdb67d60c71f19d0654d9d1df0fcd0c60648 Mon Sep 17 00:00:00 2001 From: CriticalGameEror <83310221+CriticalGameEror@users.noreply.github.com> Date: Tue, 5 Aug 2025 19:02:27 +0100 Subject: [PATCH 4/5] refactor: Changed bad request to invalid request --- src/mcp/client/streamable_http.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/mcp/client/streamable_http.py b/src/mcp/client/streamable_http.py index ce4be9a9f..f9c24f52f 100644 --- a/src/mcp/client/streamable_http.py +++ b/src/mcp/client/streamable_http.py @@ -29,6 +29,7 @@ JSONRPCRequest, JSONRPCResponse, RequestId, + INVALID_REQUEST, ) logger = logging.getLogger(__name__) @@ -268,7 +269,7 @@ async def _handle_post_request(self, ctx: RequestContext) -> None: if response.status_code == 400: if isinstance(message.root, JSONRPCRequest): - await self._send_bad_request_error( + await self._send_invalid_request_error( ctx.read_stream_writer, message.root.id, ) @@ -367,16 +368,16 @@ async def _send_session_terminated_error( session_message = SessionMessage(JSONRPCMessage(jsonrpc_error)) await read_stream_writer.send(session_message) - async def _send_bad_request_error( + async def _send_invalid_request_error( self, read_stream_writer: StreamWriter, request_id: RequestId, ) -> None: - """Send a bad request error response.""" + """Send an invalid request error response.""" jsonrpc_error = JSONRPCError( jsonrpc="2.0", id=request_id, - error=ErrorData(code=32600, message="Bad request"), + error=ErrorData(code=INVALID_REQUEST, message="Invalid request"), ) session_message = SessionMessage(JSONRPCMessage(jsonrpc_error)) await read_stream_writer.send(session_message) From cb58ee1b3ca95f84710d35d5ee73e0bcaf20f3be Mon Sep 17 00:00:00 2001 From: CriticalGameEror <83310221+CriticalGameEror@users.noreply.github.com> Date: Tue, 5 Aug 2025 21:48:53 +0100 Subject: [PATCH 5/5] refactor: minor linting improvements --- src/mcp/client/streamable_http.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mcp/client/streamable_http.py b/src/mcp/client/streamable_http.py index f9c24f52f..644760550 100644 --- a/src/mcp/client/streamable_http.py +++ b/src/mcp/client/streamable_http.py @@ -21,6 +21,7 @@ from mcp.shared._httpx_utils import McpHttpClientFactory, create_mcp_http_client from mcp.shared.message import ClientMessageMetadata, SessionMessage from mcp.types import ( + INVALID_REQUEST, ErrorData, InitializeResult, JSONRPCError, @@ -29,7 +30,6 @@ JSONRPCRequest, JSONRPCResponse, RequestId, - INVALID_REQUEST, ) logger = logging.getLogger(__name__)