Jeremiah Lowin commited on
Commit
4a59792
·
1 Parent(s): f80d248

Ensure custom routes are respected

Browse files
src/fastmcp/server/http.py CHANGED
@@ -241,6 +241,7 @@ def create_sse_app(
241
  # Add custom routes with lowest precedence
242
  if routes:
243
  server_routes.extend(routes)
 
244
 
245
  # Add middleware
246
  if middleware:
@@ -359,6 +360,7 @@ def create_streamable_http_app(
359
  # Add custom routes with lowest precedence
360
  if routes:
361
  server_routes.extend(routes)
 
362
 
363
  # Add middleware
364
  if middleware:
 
241
  # Add custom routes with lowest precedence
242
  if routes:
243
  server_routes.extend(routes)
244
+ server_routes.extend(server._additional_http_routes)
245
 
246
  # Add middleware
247
  if middleware:
 
360
  # Add custom routes with lowest precedence
361
  if routes:
362
  server_routes.extend(routes)
363
+ server_routes.extend(server._additional_http_routes)
364
 
365
  # Add middleware
366
  if middleware:
src/fastmcp/server/server.py CHANGED
@@ -854,7 +854,6 @@ class FastMCP(Generic[LifespanResultT]):
854
  auth_server_provider=self._auth_server_provider,
855
  auth_settings=self.settings.auth,
856
  debug=self.settings.debug,
857
- routes=self._additional_http_routes,
858
  middleware=middleware,
859
  )
860
 
@@ -905,7 +904,6 @@ class FastMCP(Generic[LifespanResultT]):
905
  json_response=self.settings.json_response,
906
  stateless_http=self.settings.stateless_http,
907
  debug=self.settings.debug,
908
- routes=self._additional_http_routes,
909
  middleware=middleware,
910
  )
911
  elif transport == "sse":
@@ -916,7 +914,6 @@ class FastMCP(Generic[LifespanResultT]):
916
  auth_server_provider=self._auth_server_provider,
917
  auth_settings=self.settings.auth,
918
  debug=self.settings.debug,
919
- routes=self._additional_http_routes,
920
  middleware=middleware,
921
  )
922
 
 
854
  auth_server_provider=self._auth_server_provider,
855
  auth_settings=self.settings.auth,
856
  debug=self.settings.debug,
 
857
  middleware=middleware,
858
  )
859
 
 
904
  json_response=self.settings.json_response,
905
  stateless_http=self.settings.stateless_http,
906
  debug=self.settings.debug,
 
907
  middleware=middleware,
908
  )
909
  elif transport == "sse":
 
914
  auth_server_provider=self._auth_server_provider,
915
  auth_settings=self.settings.auth,
916
  debug=self.settings.debug,
 
917
  middleware=middleware,
918
  )
919
 
tests/server/http/test_custom_routes.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+ from starlette.requests import Request
3
+ from starlette.responses import JSONResponse
4
+ from starlette.routing import Route
5
+
6
+ from fastmcp import FastMCP
7
+ from fastmcp.server.http import create_sse_app, create_streamable_http_app
8
+
9
+
10
+ class TestCustomRoutes:
11
+ @pytest.fixture
12
+ def server_with_custom_route(self):
13
+ """Create a FastMCP server with a custom route."""
14
+ server = FastMCP()
15
+
16
+ @server.custom_route("/custom-route", methods=["GET"])
17
+ async def custom_route(request: Request):
18
+ return JSONResponse({"message": "custom route"})
19
+
20
+ return server
21
+
22
+ def test_custom_routes_via_server_http_app(self, server_with_custom_route):
23
+ """Test that custom routes are included when using server.http_app()."""
24
+ # Get the app via server.http_app()
25
+ app = server_with_custom_route.http_app()
26
+
27
+ # Verify that the custom route is included
28
+ custom_route_found = False
29
+ for route in app.routes:
30
+ if isinstance(route, Route) and route.path == "/custom-route":
31
+ custom_route_found = True
32
+ break
33
+
34
+ assert custom_route_found, "Custom route was not found in app routes"
35
+
36
+ def test_custom_routes_via_streamable_http_app_direct(
37
+ self, server_with_custom_route
38
+ ):
39
+ """Test that custom routes are included when using create_streamable_http_app directly."""
40
+ # Create the app by calling the constructor function directly
41
+ app = create_streamable_http_app(
42
+ server=server_with_custom_route, streamable_http_path="/api"
43
+ )
44
+
45
+ # Verify that the custom route is included
46
+ custom_route_found = False
47
+ for route in app.routes:
48
+ if isinstance(route, Route) and route.path == "/custom-route":
49
+ custom_route_found = True
50
+ break
51
+
52
+ assert custom_route_found, "Custom route was not found in app routes"
53
+
54
+ def test_custom_routes_via_sse_app_direct(self, server_with_custom_route):
55
+ """Test that custom routes are included when using create_sse_app directly."""
56
+ # Create the app by calling the constructor function directly
57
+ app = create_sse_app(
58
+ server=server_with_custom_route, message_path="/message", sse_path="/sse"
59
+ )
60
+
61
+ # Verify that the custom route is included
62
+ custom_route_found = False
63
+ for route in app.routes:
64
+ if isinstance(route, Route) and route.path == "/custom-route":
65
+ custom_route_found = True
66
+ break
67
+
68
+ assert custom_route_found, "Custom route was not found in app routes"
69
+
70
+ def test_multiple_custom_routes(
71
+ self,
72
+ ):
73
+ """Test that multiple custom routes are included in both methods."""
74
+ server = FastMCP()
75
+
76
+ custom_paths = ["/route1", "/route2", "/route3"]
77
+
78
+ # Add multiple custom routes
79
+ for path in custom_paths:
80
+
81
+ @server.custom_route(path, methods=["GET"])
82
+ async def custom_route(request: Request):
83
+ return JSONResponse({"message": f"route {path}"})
84
+
85
+ # Test with server.http_app()
86
+ app1 = server.http_app()
87
+
88
+ # Test with direct constructor call
89
+ app2 = create_streamable_http_app(server=server, streamable_http_path="/api")
90
+
91
+ # Check all routes are in both apps
92
+ for path in custom_paths:
93
+ # Check in app1
94
+ route_in_app1 = any(
95
+ isinstance(route, Route) and route.path == path for route in app1.routes
96
+ )
97
+ assert route_in_app1, f"Route {path} not found in server.http_app()"
98
+
99
+ # Check in app2
100
+ route_in_app2 = any(
101
+ isinstance(route, Route) and route.path == path for route in app2.routes
102
+ )
103
+ assert route_in_app2, (
104
+ f"Route {path} not found in create_streamable_http_app()"
105
+ )
tests/server/{test_http_dependencies.py → http/test_http_dependencies.py} RENAMED
File without changes
tests/server/{test_http_middleware.py → http/test_http_middleware.py} RENAMED
File without changes