From b42b35a983ca4689659e060d4f2affeb51af3319 Mon Sep 17 00:00:00 2001 From: eyeonyou Date: Thu, 27 Mar 2025 17:22:32 +0300 Subject: [PATCH 01/10] fix(): SSE client handling of nested path URLs Fix issue with SSE client failing when connecting to servers with nested paths (e.g., http://domain/path1/path2/sse). Previously, the client worked only with simple URLs like http://domain/sse. The fix extracts the base path segment between domain and /sse endpoint using regex, then correctly constructs the response URL by preserving the path structure. This ensures proper communication with MCP servers hosted under non-root paths. Without this fix, clients couldn't connect to SSE servers deployed under nested paths, as the endpoint URLs for posting messages were constructed incorrectly. --- src/mcp/client/sse.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mcp/client/sse.py b/src/mcp/client/sse.py index 4f6241a7..a9542861 100644 --- a/src/mcp/client/sse.py +++ b/src/mcp/client/sse.py @@ -2,6 +2,7 @@ from contextlib import asynccontextmanager from typing import Any from urllib.parse import urljoin, urlparse +import re import anyio import httpx @@ -61,7 +62,9 @@ async def sse_reader( logger.debug(f"Received SSE event: {sse.event}") match sse.event: case "endpoint": - endpoint_url = urljoin(url, sse.data) + base_path = re.search(r"https?://[^/]+/(.+)/sse$", url) + base_path = base_path.group(1) if base_path else "" + endpoint_url = urljoin(url, base_path + sse.data) logger.info( f"Received endpoint URL: {endpoint_url}" ) From 5a4d42053b48d1101f482b2de66699a5b8933c31 Mon Sep 17 00:00:00 2001 From: eyeonyou Date: Thu, 27 Mar 2025 17:43:59 +0300 Subject: [PATCH 02/10] style: format max-line --- src/mcp/client/sse.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/mcp/client/sse.py b/src/mcp/client/sse.py index a9542861..ffbb4ba7 100644 --- a/src/mcp/client/sse.py +++ b/src/mcp/client/sse.py @@ -62,9 +62,15 @@ async def sse_reader( logger.debug(f"Received SSE event: {sse.event}") match sse.event: case "endpoint": - base_path = re.search(r"https?://[^/]+/(.+)/sse$", url) - base_path = base_path.group(1) if base_path else "" - endpoint_url = urljoin(url, base_path + sse.data) + base_path = re.search( + r"https?://[^/]+/(.+)/sse$", url + ) + base_path = ( + base_path.group(1) if base_path else "" + ) + endpoint_url = urljoin( + url, base_path + sse.data + ) logger.info( f"Received endpoint URL: {endpoint_url}" ) From 21a775f21e12e2a03b7141acc4bc8d8ac99bf82e Mon Sep 17 00:00:00 2001 From: eyeonyou Date: Fri, 28 Mar 2025 08:11:35 +0300 Subject: [PATCH 03/10] fix: imports --- src/mcp/client/sse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mcp/client/sse.py b/src/mcp/client/sse.py index ffbb4ba7..f7822852 100644 --- a/src/mcp/client/sse.py +++ b/src/mcp/client/sse.py @@ -1,8 +1,8 @@ import logging +import re from contextlib import asynccontextmanager from typing import Any from urllib.parse import urljoin, urlparse -import re import anyio import httpx From 9a58b30886d3e044a2a206de31e287a4d7c962e1 Mon Sep 17 00:00:00 2001 From: Jonathan Lima Date: Mon, 7 Apr 2025 17:38:24 -0300 Subject: [PATCH 04/10] fixing /mcp case prefix on langchain --- src/mcp/client/sse.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/mcp/client/sse.py b/src/mcp/client/sse.py index f7822852..29836132 100644 --- a/src/mcp/client/sse.py +++ b/src/mcp/client/sse.py @@ -62,20 +62,22 @@ async def sse_reader( logger.debug(f"Received SSE event: {sse.event}") match sse.event: case "endpoint": + url_parsed = urlparse(url) + base_path = re.search( - r"https?://[^/]+/(.+)/sse$", url + r"https?://[^/]+/(.+?)(?:/mcp)?/sse$", url ) base_path = ( base_path.group(1) if base_path else "" ) endpoint_url = urljoin( - url, base_path + sse.data + url_parsed.scheme + "://" + url_parsed.netloc, + base_path + sse.data ) logger.info( f"Received endpoint URL: {endpoint_url}" ) - url_parsed = urlparse(url) endpoint_parsed = urlparse(endpoint_url) if ( url_parsed.netloc != endpoint_parsed.netloc From 8e76e8a5c6fb48a7513e5173041694cafc26901d Mon Sep 17 00:00:00 2001 From: eyeonyou Date: Tue, 8 Apr 2025 10:45:23 +0300 Subject: [PATCH 05/10] style: Added noqa to avoid max-line of code --- src/mcp/client/sse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mcp/client/sse.py b/src/mcp/client/sse.py index 29836132..c41c414e 100644 --- a/src/mcp/client/sse.py +++ b/src/mcp/client/sse.py @@ -71,7 +71,7 @@ async def sse_reader( base_path.group(1) if base_path else "" ) endpoint_url = urljoin( - url_parsed.scheme + "://" + url_parsed.netloc, + url_parsed.scheme + "://" + url_parsed.netloc, # noqa: E501 base_path + sse.data ) logger.info( From e6d5b00991a9abcfe2469e2fed929d0113ad6e3a Mon Sep 17 00:00:00 2001 From: sebin1213 <73850629+sebin1213@users.noreply.github.com> Date: Wed, 9 Apr 2025 02:02:49 +0000 Subject: [PATCH 06/10] Fix generate accurate session_uri for nested SSE paths --- src/mcp/client/sse.py | 16 +++------------- src/mcp/server/sse.py | 7 +++++-- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/mcp/client/sse.py b/src/mcp/client/sse.py index c41c414e..0194c02a 100644 --- a/src/mcp/client/sse.py +++ b/src/mcp/client/sse.py @@ -1,5 +1,4 @@ import logging -import re from contextlib import asynccontextmanager from typing import Any from urllib.parse import urljoin, urlparse @@ -62,21 +61,12 @@ async def sse_reader( logger.debug(f"Received SSE event: {sse.event}") match sse.event: case "endpoint": - url_parsed = urlparse(url) - - base_path = re.search( - r"https?://[^/]+/(.+?)(?:/mcp)?/sse$", url - ) - base_path = ( - base_path.group(1) if base_path else "" - ) - endpoint_url = urljoin( - url_parsed.scheme + "://" + url_parsed.netloc, # noqa: E501 - base_path + sse.data - ) + endpoint_url = urljoin(url, sse.data) logger.info( f"Received endpoint URL: {endpoint_url}" ) + + url_parsed = urlparse(url) endpoint_parsed = urlparse(endpoint_url) if ( diff --git a/src/mcp/server/sse.py b/src/mcp/server/sse.py index d051c25b..dbb40bed 100644 --- a/src/mcp/server/sse.py +++ b/src/mcp/server/sse.py @@ -94,8 +94,11 @@ async def connect_sse(self, scope: Scope, receive: Receive, send: Send): read_stream_writer, read_stream = anyio.create_memory_object_stream(0) write_stream, write_stream_reader = anyio.create_memory_object_stream(0) - session_id = uuid4() - session_uri = f"{quote(self._endpoint)}?session_id={session_id.hex}" + request_path = scope["path"] + match = re.match(r"^/([^/]+(?:/mcp)?)/sse$", request_path) + mount_prefix = match.group(1) if match else "" + session_uri = f"/{quote(mount_prefix)}{quote(self._endpoint)}?session_id={session_id.hex}" + self._read_stream_writers[session_id] = read_stream_writer logger.debug(f"Created new session with ID: {session_id}") From 1ac7b63fe648fb2c8bcf87bed0797da79650881e Mon Sep 17 00:00:00 2001 From: sebin1213 <73850629+sebin1213@users.noreply.github.com> Date: Wed, 9 Apr 2025 11:13:29 +0900 Subject: [PATCH 07/10] Fix generate accurate session_uri for nested SSE paths --- src/mcp/server/sse.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mcp/server/sse.py b/src/mcp/server/sse.py index dbb40bed..a5119037 100644 --- a/src/mcp/server/sse.py +++ b/src/mcp/server/sse.py @@ -94,6 +94,7 @@ async def connect_sse(self, scope: Scope, receive: Receive, send: Send): read_stream_writer, read_stream = anyio.create_memory_object_stream(0) write_stream, write_stream_reader = anyio.create_memory_object_stream(0) + session_id = uuid4() request_path = scope["path"] match = re.match(r"^/([^/]+(?:/mcp)?)/sse$", request_path) mount_prefix = match.group(1) if match else "" From 7cece5e8e6afeb97eb4e81988acb948a4d512ac5 Mon Sep 17 00:00:00 2001 From: sebin1213 <73850629+sebin1213@users.noreply.github.com> Date: Wed, 9 Apr 2025 13:15:47 +0900 Subject: [PATCH 08/10] Fix generate accurate session_uri for nested SSE paths --- src/mcp/server/sse.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mcp/server/sse.py b/src/mcp/server/sse.py index a5119037..ad6059c3 100644 --- a/src/mcp/server/sse.py +++ b/src/mcp/server/sse.py @@ -37,6 +37,7 @@ async def handle_sse(request): from urllib.parse import quote from uuid import UUID, uuid4 +import re import anyio from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream from pydantic import ValidationError From 3728328497814e046293e5ffc8ac8571968e9e2d Mon Sep 17 00:00:00 2001 From: eyeonyou Date: Wed, 9 Apr 2025 22:32:26 +0300 Subject: [PATCH 09/10] style: format to avoid max-line validation error --- src/mcp/server/sse.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mcp/server/sse.py b/src/mcp/server/sse.py index ad6059c3..15c90f83 100644 --- a/src/mcp/server/sse.py +++ b/src/mcp/server/sse.py @@ -97,9 +97,12 @@ async def connect_sse(self, scope: Scope, receive: Receive, send: Send): session_id = uuid4() request_path = scope["path"] + match = re.match(r"^/([^/]+(?:/mcp)?)/sse$", request_path) mount_prefix = match.group(1) if match else "" - session_uri = f"/{quote(mount_prefix)}{quote(self._endpoint)}?session_id={session_id.hex}" + + session_uri = f"/{quote(mount_prefix)}{quote(self._endpoint)}" + session_uri += f"?session_id={session_id.hex}" self._read_stream_writers[session_id] = read_stream_writer logger.debug(f"Created new session with ID: {session_id}") From e1ca588e07e83a8a2178582a48235935861b2f98 Mon Sep 17 00:00:00 2001 From: eyeonyou Date: Wed, 9 Apr 2025 22:35:30 +0300 Subject: [PATCH 10/10] style: reorganize imports --- src/mcp/server/sse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mcp/server/sse.py b/src/mcp/server/sse.py index 15c90f83..6ec4c165 100644 --- a/src/mcp/server/sse.py +++ b/src/mcp/server/sse.py @@ -32,12 +32,12 @@ async def handle_sse(request): """ import logging +import re from contextlib import asynccontextmanager from typing import Any from urllib.parse import quote from uuid import UUID, uuid4 -import re import anyio from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream from pydantic import ValidationError