diff --git a/litellm/proxy/_experimental/mcp_server/oauth_utils.py b/litellm/proxy/_experimental/mcp_server/oauth_utils.py index 4d5813dbc5b..96647edad77 100644 --- a/litellm/proxy/_experimental/mcp_server/oauth_utils.py +++ b/litellm/proxy/_experimental/mcp_server/oauth_utils.py @@ -129,6 +129,7 @@ def get_request_base_url(request: Request) -> str: if x_forwarded_port and ":" not in netloc: netloc = f"{netloc}:{x_forwarded_port}" + netloc = _strip_default_port(scheme, netloc) return urlunparse((scheme, netloc, parsed.path, "", "", "")) diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index b4a26c911dc..54c7eb92e1a 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -1103,6 +1103,38 @@ async def test_token_endpoint_respects_x_forwarded_host(): None, "https://external.com", ), + # Case 15: X-Forwarded-Port 443 with HTTPS must NOT appear in the URL + ( + "http://localhost:4000/", + "https", + "proxy.example.com", + "443", + "https://proxy.example.com", + ), + # Case 16: X-Forwarded-Port 80 with HTTP must NOT appear in the URL + ( + "http://localhost:4000/", + "http", + "proxy.example.com", + "80", + "http://proxy.example.com", + ), + # Case 17: X-Forwarded-Port 443 without X-Forwarded-Host (portless base) + ( + "http://internal.local/", + "https", + None, + "443", + "https://internal.local", + ), + # Case 18: Non-default port is preserved + ( + "http://localhost:4000/", + "https", + "proxy.example.com", + "8443", + "https://proxy.example.com:8443", + ), ], ) def test_get_request_base_url_comprehensive(