Skip to content

Fix 307 Temporary Redirect when use streamable_http #781

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

chi2liu
Copy link

@chi2liu chi2liu commented May 22, 2025

The default mount path of Streamable HTTP is /mcp, but in some environments, a 307 Temporary Redirect to /mcp/mcp/ will appear, causing the request to hang or fail. When the streamable_http path is set to /mcp/, everything works fine.

Code Analysis
Mount Path

In the server.py and simple-streamablehttp-stateless examples, the ASGI routes are Mount("/mcp", app=handle_streamable_http), that is, there is no trailing slash.
In the Settings class of server.py, the default value of streamable_http_path is "/mcp" (also without a slash).
Redirection Issue

Mount("/mcp", ...) route of Starlette/FastAPI, if /mcp is requested, will automatically 307 redirect to /mcp/ (with a slash).
But if the client requests /mcp/ and the server only mounts /mcp, a redirect chain of /mcp/mcp/ will appear in some environments, resulting in "unreachable".
This is related to the "strict slash matching" of ASGI routing.
Solution

Recommended practice: Always use /mcp/ as the mounting path (with a slash) and let the client request /mcp/, so there will be no redirection problem.
Or, when mounting /mcp, make sure the client only requests /mcp without a slash.

Motivation and Context

How Has This Been Tested?

Breaking Changes

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

@chi2liu
Copy link
Author

chi2liu commented May 22, 2025

#732

@chi2liu
Copy link
Author

chi2liu commented May 22, 2025

when we set the default /mcp, the log will be, in some env will

INFO:     Started server process [55547]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
INFO:     10.183.170.230:53048 - "POST /mcp HTTP/1.1" 307 Temporary Redirect
INFO:     10.183.170.230:53054 - "POST /mcp/ HTTP/1.1" 200 OK
INFO:     10.183.170.230:53048 - "POST /mcp HTTP/1.1" 307 Temporary Redirect
INFO:     10.183.170.230:53054 - "POST /mcp/ HTTP/1.1" 202 Accepted
INFO:     10.183.170.230:53048 - "GET /mcp HTTP/1.1" 307 Temporary Redirect
INFO:     10.183.170.230:53054 - "GET /mcp/ HTTP/1.1" 200 OK
INFO:     10.183.170.230:34296 - "POST /mcp HTTP/1.1" 307 Temporary Redirect
INFO:     10.183.170.230:34312 - "POST /mcp/ HTTP/1.1" 200 OK

in some env will be hanging, because 307 Temporary Redirect to /byoa/mcp/mcp/, this is unreachable

INFO:     Started server process [35]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
INFO:     100.67.31.17:58472 - "POST /mcp HTTP/1.1" 307 Temporary Redirect
INFO:     100.67.30.95:57600 - "GET /mcp HTTP/1.1" 307 Temporary Redirect

when we set the streamable_http to '/mcp/', it will be good, as the log:

[05/22/25 01:52:28] INFO     Starting server "StatelessServer"...                                                                server.py:214
INFO:     Started server process [80154]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
INFO:     10.183.170.230:36166 - "POST /mcp/ HTTP/1.1" 200 OK
INFO:     10.183.170.230:36166 - "POST /mcp/ HTTP/1.1" 202 Accepted
INFO:     10.183.170.230:36182 - "GET /mcp/ HTTP/1.1" 200 OK
INFO:     10.183.170.230:33648 - "POST /mcp/ HTTP/1.1" 200 OK

@chi2liu
Copy link
Author

chi2liu commented May 22, 2025

@chi2liu
Copy link
Author

chi2liu commented May 22, 2025

@ihrpr

Copy link
Contributor

@ihrpr ihrpr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for identifying and reporting this redirect issue! However, I don't think changing the default
streamable_http_path is the right approach here.

The core issue: This change creates inconsistency across the codebase rather than solving the root problem.

  • All client code constructs URLs with /mcp (no trailing slash) - see examples/clients/simple-auth-client/main.py:344
  • All example servers mount at /mcp (no trailing slash) - see both simple-streamablehttp examples
  • All tests use /mcp (no trailing slash) consistently - see tests/shared/test_streamable_http.py:238
  • Only FastMCP would have a different default with this change

Problems with this approach:

  1. Breaking consistency: Makes FastMCP behave differently from all examples and existing usage patterns
  2. Incomplete fix: The examples and tests still use /mcp without trailing slash, so the inconsistency remains
  3. Wrong direction: The entire ecosystem already standardized on /mcp - we shouldn't change that

Alterative:

  1. Keep the default as /mcp and fix the ASGI mounting logic to handle redirects properly
  2. Add logic to FastMCP to handle both /mcp and /mcp/ transparently at the mount level
  3. Document the trailing slash behavior so users can choose the approach that works for their environment

The redirect issue you've identified is real and needs fixing, but changing the default path creates more problems than
it solves. Let's find a solution that maintains consistency while addressing the underlying ASGI routing behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants