Jeremiah Lowin commited on
Commit
86eb39a
·
1 Parent(s): bf9967e

Use trailing slashes

Browse files
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
- # Create an ASGI app wrapper that normalizes paths and handles both /mcp and /mcp/
314
- class PathNormalizingASGIApp:
315
- def __init__(self, app):
316
- self.app = app
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
- # Create path pattern without trailing slash
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 app with RequireAuthMiddleware
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
- Route(
356
- path_pattern + "/",
357
- endpoint=wrapped_app,
358
- methods=["GET", "POST"]
359
  )
360
  )
361
  else:
362
- # No auth required - add routes for both with and without trailing slash
363
- server_routes.append(
364
- Route(
365
- path_pattern,
366
- endpoint=normalized_handler,
367
- methods=["GET", "POST"]
368
- )
369
- )
370
  server_routes.append(
371
- Route(
372
- path_pattern + "/",
373
- endpoint=normalized_handler,
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.rstrip("/")
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