Skip to content

Commit 1d9632b

Browse files
vectorstainVincenzo Maria Calandrachi2liu
authored
Fix 307 temporary redirect refactor (#4)
* Refactor FastMCP routing to use Router and streamline request handling * Add async support to token validation test and enhance metadata snapshot assertions * Fix tests * Fix tests --------- Co-authored-by: Vincenzo Maria Calandra <vincenzomariacalandra@MacBook-Pro-di-Vincenzo.local> Co-authored-by: 633WHU <cliu_whu@yeah.net>
1 parent 04c4bcf commit 1d9632b

File tree

2 files changed

+37
-8
lines changed

2 files changed

+37
-8
lines changed

src/mcp/server/fastmcp/server.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ async def sse_endpoint(request: Request) -> Response:
770770
def streamable_http_app(self) -> Starlette:
771771
"""Return an instance of the StreamableHTTP server app."""
772772
from starlette.middleware import Middleware
773-
from starlette.routing import Mount
773+
from starlette.routing import Mount, Router
774774

775775
# Create session manager on first call (lazy initialization)
776776
if self._session_manager is None:
@@ -787,12 +787,24 @@ async def handle_streamable_http(
787787
) -> None:
788788
await self.session_manager.handle_request(scope, receive, send)
789789

790-
# Create routes
791-
routes: list[Route | Mount] = []
790+
async def streamable_http_endpoint(request: Request):
791+
return await handle_streamable_http(request.scope, request.receive, request._send) # type: ignore[reportPrivateUsage]
792+
793+
# Normalize the main path (no trailing slash)
794+
_main_path = self.settings.streamable_http_path.removesuffix("/")
795+
796+
streamable_router = Router(
797+
routes=[
798+
Route("/", endpoint=streamable_http_endpoint, methods=["GET", "POST"]),
799+
],
800+
redirect_slashes=False,
801+
)
802+
803+
routes: list[Route | Mount ] = []
792804
middleware: list[Middleware] = []
793805
required_scopes = []
794806

795-
# Add auth endpoints if auth provider is configured
807+
# Auth endpoints if auth provider is configured
796808
if self._auth_server_provider:
797809
assert self.settings.auth
798810
from mcp.server.auth.routes import create_auth_routes
@@ -817,18 +829,21 @@ async def handle_streamable_http(
817829
revocation_options=self.settings.auth.revocation_options,
818830
)
819831
)
832+
820833
routes.append(
821834
Mount(
822-
self.settings.streamable_http_path,
823-
app=RequireAuthMiddleware(handle_streamable_http, required_scopes),
835+
_main_path,
836+
app=RequireAuthMiddleware(
837+
streamable_router, required_scopes
838+
),
824839
)
825840
)
826841
else:
827842
# Auth is disabled, no wrapper needed
828843
routes.append(
829844
Mount(
830-
self.settings.streamable_http_path,
831-
app=handle_streamable_http,
845+
_main_path,
846+
app=streamable_router,
832847
)
833848
)
834849

tests/server/fastmcp/test_server.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,20 @@ async def test_starlette_routes_with_mount_path(self):
124124
mount_routes[0].path == "/messages"
125125
), "Mount route path should be /messages"
126126

127+
mcp = FastMCP()
128+
app = mcp.streamable_http_app()
129+
130+
# Find routes by type
131+
streamable_routes = [r for r in app.routes if isinstance(r, Mount)]
132+
133+
# Verify routes exist
134+
assert len(streamable_routes) == 1, "Should have two streamable routes"
135+
136+
# Verify path values
137+
assert (
138+
streamable_routes[0].path == "/mcp"
139+
), "Streamable route path should be /mcp"
140+
127141
@pytest.mark.anyio
128142
async def test_non_ascii_description(self):
129143
"""Test that FastMCP handles non-ASCII characters in descriptions correctly"""

0 commit comments

Comments
 (0)