-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Description
Initial Checks
- I confirm that I'm using the latest version of MCP Python SDK
- I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue
Description
When using the MCP Python client with SSE transport, the sse_client helper only accepts the SSE endpoint:
async with sse_client(url=mcp_server_sse_url, headers=headers, timeout=timeout) as streams:
async with ClientSession(*streams) as session:
await session.initialize()
return await session.call_tool(name=name, arguments=arguments)Internally, the MCP client derives the messages endpoint automatically from the SSE URL and sse.data.
However, this assumption breaks in deployments where reverse proxies (e.g., Nginx) inject path prefixes that the backend service is unaware of.
Example deployment
Externally exposed endpoints after reverse proxying:
https://example.com/api/v1/sse
https://example.com/api/v1/messages/{session_id}
Internally, the MCP server only defines:
- SSE endpoint:
/v1/sse - Messages POST endpoint:
/v1/messages/
The /api prefix is injected by the reverse proxy.
In this situation:
- The client connects sse endpoint correct (Because we are providing the url):
https://example.com/api/v1/sse - But the MCP client internally derives the messages endpoint as:
https://example.com/v1/messages/{session_id} -> Based on sse.data
instead of:
https://example.com/api/v1/messages/{session_id}
Because the client assumes a fixed base path.
Current limitation
The sse_client interface does not allow explicitly providing a messages_url, which makes it impossible to support deployments where:
- the base path is rewritten by a reverse proxy
- the backend service is unaware of the external prefix
Proposed improvement
Allow explicitly specifying the messages endpoint when creating the SSE client:
async with sse_client(
url="https://example.com/api/v1/sse",
messages_url="https://example.com/api/v1/messages",
headers=headers,
timeout=timeout
) as streams:Behavior:
- If
messages_urlis provided → use it directly - Otherwise → fall back to the current behavior of deriving it from the SSE URL
This change would allow the MCP Python client to work correctly in reverse-proxy deployments, versioned APIs, and gateway setups, while maintaining backward compatibility.
Example Code
Python & MCP Python SDK
Python 3.13.12
mcp>=1.26.0