-
Notifications
You must be signed in to change notification settings - Fork 822
Add base_path support for SSE endpoint subpath routing via nginx or other http middleware #415
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
Conversation
How is this different from the existing |
Also please see #370, we explicitly reject the approach taken in mcp 540 as a flawed approach. |
You can take a look at my description, this is different from |
modelcontextprotocol/python-sdk#659 This does not solve the Nginx proxy issue. Using nginx for proxying is a common practice. Perhaps this configuration should be called |
The normalized path is passed into the SSE ServerTransport, I fail to see how this is any different than overriding the ASGI root path behavior that has already been implemented. We have tests for the exact situation you describe at https://github.com/jlowin/fastmcp/blob/main/tests/client/test_sse.py#L97-L128. |
This is not the same as baseurl. When the server is mounted under the /mcp path and routed through Nginx to that path, the client would need to request http://example.com/mcp/mcp/sse to access the service, requiring two /mcp paths. What we want to achieve is that when Nginx routes an MCP server to the /mcp path, the service can correctly connect to SSE and return the correct messages endpoint. I understand your point about using ASGI for routing, but what I’m referring to is using Nginx or other HTTP middleware for path-based routing. This is a common requirement, often implemented by using Nginx to route subpaths and share the same domain. You can check out how baseurl is applied in Jupyter or root_url in Grafana for similar use cases. |
update: |
Hi @LiangYang666, thanks for highlighting the scenario of deploying behind a reverse proxy. I've reviewed this PR and unfortunately it doesn't solve that problem and also introduces new bugs. In the current implementation, the A solution would be to provide the As such, there are two clear patterns that are already supported. The first is to mount the entire SSE app at a prefix like mcp = FastMCP()
sse_app = mcp.create_sse_app()
app = Starlette(routes=[Mount("/mcp", app=sse_app)]) Whether done with Starlette mount as above or through a different routing solution, any request to /mcp is now processed to the app correctly, including to /mcp/messages (which the app will receive as just /messages) An alternative approach is mcp = FastMCP()
app = mcp.create_sse_app(path='/mcp/sse', message_path='/mcp/messages') This creates a similar outcome, except that now the app itself expects the /mcp/ prefix to be part of the request URL. This can be handled by configuring your proxy appropriately. Lastly, regarding comparisons to Jupyter's If you have a new case that can not be handled by either of the above situations or through manually supplying e.g. a root_path to your application or uvicorn instance, then we can reconsider it as a bug report that can be tested. However, your current implementation is broken and, even if the base URL were passed to the messages mount to fix it, lacks clear motivation over why a third keyword argument is better than concatenating the message url manually. |
@jlowin You are incorrect. This does not resolve the issue of using Nginx for proxying. Introducing base_path will not cause the problem you mentioned because Nginx has already mounted the application to If I implement it the way you suggested, i.e., Additionally, if it is not implemented the way I described, it will not be possible to use Nginx or similar tools to forward the MCP server. |
However, I later tested using ASGI's root_path, and it works. Like this: mcp = FastMCP()
sse_app = mcp.sse_app()
if __name__ == "__main__":
uvicorn.run(sse_app, host="0.0.0.0", port=8000, root_path="/mcp") Therefore, this can also resolve the issue. Thank you. However, I still believe that root_path should be considered as a parameter for the MCP server. Please think about it. |
Description:
This PR introduces a base_path parameter to the SSE configuration in MCP, similar to the baseurl concept commonly found in other web applications. eg: base_url in jupyter、base_url in filebrowser or root_url in grafana
Background & Reasoning:
When using Nginx or other HTTP proxies to route requests to MCP, the endpoint paths can become inconsistent. For example, when routing
/mcp
to a specific MCP service, the SSE endpoint can be set ashttps://example.com/mcp/sse
. However, the client currently receives a messages endpoint ashttps://example.com/messages/
, which is incorrect. The correct endpoint should behttps://example.com/mcp/messages/
.By introducing the mount_path parameter, users can set a base path (e.g., /mcp). This ensures that the SSE server correctly adjusts its internal endpoints to match the proxy configuration, providing accurate endpoint paths to clients.
Implementation Note:
Initially, the parameter was considered to be named baseurl, but to maintain consistency with another related repository that implemented a similar change, the parameter is named mount_path. PR
Example Usage:
This change improves compatibility when deploying MCP behind reverse proxies or HTTP routers.