Spaces:
Running
Running
Jeremiah Lowin commited on
Commit ·
86eb39a
1
Parent(s): bf9967e
Use trailing slashes
Browse files- src/fastmcp/server/http.py +14 -49
- src/fastmcp/settings.py +2 -2
src/fastmcp/server/http.py
CHANGED
|
@@ -310,28 +310,12 @@ def create_streamable_http_app(
|
|
| 310 |
# Re-raise other RuntimeErrors if they don't match the specific message
|
| 311 |
raise
|
| 312 |
|
| 313 |
-
#
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
async def __call__(self, scope, receive, send):
|
| 319 |
-
# Normalize path to remove trailing slash for MCP SDK
|
| 320 |
-
if scope["type"] == "http":
|
| 321 |
-
path = scope["path"]
|
| 322 |
-
if path.endswith("/") and len(path) > 1:
|
| 323 |
-
scope = dict(scope)
|
| 324 |
-
scope["path"] = path.rstrip("/")
|
| 325 |
-
|
| 326 |
-
await self.app(scope, receive, send)
|
| 327 |
-
|
| 328 |
-
# Create the path-normalizing wrapper
|
| 329 |
-
normalized_handler = PathNormalizingASGIApp(handle_streamable_http)
|
| 330 |
|
| 331 |
-
#
|
| 332 |
-
path_pattern = streamable_http_path.rstrip("/")
|
| 333 |
-
|
| 334 |
-
# Add StreamableHTTP routes with or without auth - add both with and without trailing slash
|
| 335 |
if auth:
|
| 336 |
auth_middleware, auth_routes, required_scopes = (
|
| 337 |
setup_auth_middleware_and_routes(auth)
|
|
@@ -340,38 +324,19 @@ def create_streamable_http_app(
|
|
| 340 |
server_routes.extend(auth_routes)
|
| 341 |
server_middleware.extend(auth_middleware)
|
| 342 |
|
| 343 |
-
# Auth is enabled, wrap
|
| 344 |
-
wrapped_app = RequireAuthMiddleware(normalized_handler, required_scopes)
|
| 345 |
-
|
| 346 |
-
# Add routes for both with and without trailing slash
|
| 347 |
-
server_routes.append(
|
| 348 |
-
Route(
|
| 349 |
-
path_pattern,
|
| 350 |
-
endpoint=wrapped_app,
|
| 351 |
-
methods=["GET", "POST"]
|
| 352 |
-
)
|
| 353 |
-
)
|
| 354 |
server_routes.append(
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
methods=["GET", "POST"]
|
| 359 |
)
|
| 360 |
)
|
| 361 |
else:
|
| 362 |
-
# No auth required
|
| 363 |
-
server_routes.append(
|
| 364 |
-
Route(
|
| 365 |
-
path_pattern,
|
| 366 |
-
endpoint=normalized_handler,
|
| 367 |
-
methods=["GET", "POST"]
|
| 368 |
-
)
|
| 369 |
-
)
|
| 370 |
server_routes.append(
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
methods=["GET", "POST"]
|
| 375 |
)
|
| 376 |
)
|
| 377 |
|
|
@@ -400,6 +365,6 @@ def create_streamable_http_app(
|
|
| 400 |
# Store the FastMCP server instance on the Starlette app state
|
| 401 |
app.state.fastmcp_server = server
|
| 402 |
|
| 403 |
-
app.state.path = streamable_http_path
|
| 404 |
|
| 405 |
return app
|
|
|
|
| 310 |
# Re-raise other RuntimeErrors if they don't match the specific message
|
| 311 |
raise
|
| 312 |
|
| 313 |
+
# Ensure the streamable_http_path ends with a trailing slash to avoid automatic redirects
|
| 314 |
+
# when mounting the application
|
| 315 |
+
if not streamable_http_path.endswith("/"):
|
| 316 |
+
streamable_http_path = streamable_http_path + "/"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 317 |
|
| 318 |
+
# Add StreamableHTTP routes with or without auth
|
|
|
|
|
|
|
|
|
|
| 319 |
if auth:
|
| 320 |
auth_middleware, auth_routes, required_scopes = (
|
| 321 |
setup_auth_middleware_and_routes(auth)
|
|
|
|
| 324 |
server_routes.extend(auth_routes)
|
| 325 |
server_middleware.extend(auth_middleware)
|
| 326 |
|
| 327 |
+
# Auth is enabled, wrap endpoint with RequireAuthMiddleware
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 328 |
server_routes.append(
|
| 329 |
+
Mount(
|
| 330 |
+
streamable_http_path,
|
| 331 |
+
app=RequireAuthMiddleware(handle_streamable_http, required_scopes),
|
|
|
|
| 332 |
)
|
| 333 |
)
|
| 334 |
else:
|
| 335 |
+
# No auth required
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
server_routes.append(
|
| 337 |
+
Mount(
|
| 338 |
+
streamable_http_path,
|
| 339 |
+
app=handle_streamable_http,
|
|
|
|
| 340 |
)
|
| 341 |
)
|
| 342 |
|
|
|
|
| 365 |
# Store the FastMCP server instance on the Starlette app state
|
| 366 |
app.state.fastmcp_server = server
|
| 367 |
|
| 368 |
+
app.state.path = streamable_http_path
|
| 369 |
|
| 370 |
return app
|
src/fastmcp/settings.py
CHANGED
|
@@ -192,9 +192,9 @@ class Settings(BaseSettings):
|
|
| 192 |
# HTTP settings
|
| 193 |
host: str = "127.0.0.1"
|
| 194 |
port: int = 8000
|
| 195 |
-
sse_path: str = "/sse"
|
| 196 |
message_path: str = "/messages/"
|
| 197 |
-
streamable_http_path: str = "/mcp"
|
| 198 |
debug: bool = False
|
| 199 |
|
| 200 |
# error handling
|
|
|
|
| 192 |
# HTTP settings
|
| 193 |
host: str = "127.0.0.1"
|
| 194 |
port: int = 8000
|
| 195 |
+
sse_path: str = "/sse/"
|
| 196 |
message_path: str = "/messages/"
|
| 197 |
+
streamable_http_path: str = "/mcp/"
|
| 198 |
debug: bool = False
|
| 199 |
|
| 200 |
# error handling
|