Skip to content

sse_app() ignores mount prefix, resulting in 404 from client #412

Open
@allieus

Description

@allieus

Describe the bug

When mounting sse_app() from FastMCP with a URL prefix using Starlette’s Mount, the SSE stream still returns the default /messages/ endpoint without the prefix. This causes the MCP client to resolve an incorrect URL (e.g., /messages/ instead of /mcp/messages/), resulting in a 404 error.

To Reproduce

Steps to reproduce the behavior:

  1. Define an MCP server as below:
from mcp.server.fastmcp import FastMCP
from starlette.applications import Starlette
from starlette.routing import Mount

mcp = FastMCP(
    # sse_path="/sse",  # default
    # message_path="/messages/",  # default
)


application = Starlette(
    routes=[
        Mount("/mcp", app=mcp.sse_app()),  # HERE !!!
    ]
)
  1. Start the server and navigate to http://127.0.0.1:8000/mcp/sse in your browser
  2. Observe the response in the browser. You will receive an SSE event like:
event: endpoint
data: /messages/?session_id=...

At this point, the MCP client performs a urljoin operation between the SSE URL (http://127.0.0.1:8000/mcp/sse) and the endpoint path (/messages/). This causes the resolved endpoint URL to become http://127.0.0.1:8000/messages/.

  1. The client tries to connect to http://127.0.0.1:8000/messages/, which results in a 404.

Expected behavior

The SSE stream should return the correct full path reflecting the prefix, e.g.:

data: /mcp/messages/?session_id=...

This would allow the client to connect to the actual valid message endpoint.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: macOS
  • Browser : Arc
  • Version : 1.87.1 (60573)

Smartphone (please complete the following information):

  • Device: N/A
  • OS: N/A
  • Browser : N/A
  • Version : N/A

Additional context

This behavior seems to originate from hardcoded endpoint generation inside sse_app():

session_uri = f"{quote(self._endpoint)}?session_id={session_id.hex}"

It would be great to have support for specifying a prefix in sse_app() or for the prefix to be auto-detected from the ASGI scope.

Thank you!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions