fix: added RFC RECOMMENDED property(scopes_supported) to protected resource and authorization server metadata

This commit is contained in:
Eric84626
2025-12-20 13:22:29 +08:00
parent c1954952c6
commit 684fba42ea
2 changed files with 68 additions and 3 deletions
@@ -398,8 +398,14 @@ async def callback(code: str, state: str):
async def oauth_protected_resource_mcp(
request: Request, mcp_server_name: Optional[str] = None
):
from litellm.proxy._experimental.mcp_server.mcp_server_manager import (
global_mcp_server_manager,
)
# Get the correct base URL considering X-Forwarded-* headers
request_base_url = get_request_base_url(request)
mcp_server: Optional[MCPServer] = None
if mcp_server_name:
mcp_server = global_mcp_server_manager.get_mcp_server_by_name(mcp_server_name)
return {
"authorization_servers": [
(
@@ -413,6 +419,7 @@ async def oauth_protected_resource_mcp(
if mcp_server_name
else f"{request_base_url}/mcp"
), # this is what Claude will call
"scopes_supported": mcp_server.scopes if mcp_server else [],
}
"""
@@ -428,6 +435,9 @@ async def oauth_protected_resource_mcp(
async def oauth_authorization_server_mcp(
request: Request, mcp_server_name: Optional[str] = None
):
from litellm.proxy._experimental.mcp_server.mcp_server_manager import (
global_mcp_server_manager,
)
# Get the correct base URL considering X-Forwarded-* headers
request_base_url = get_request_base_url(request)
@@ -442,16 +452,21 @@ async def oauth_authorization_server_mcp(
else f"{request_base_url}/token"
)
mcp_server: Optional[MCPServer] = None
if mcp_server_name:
mcp_server = global_mcp_server_manager.get_mcp_server_by_name(mcp_server_name)
return {
"issuer": request_base_url, # point to your proxy
"authorization_endpoint": authorization_endpoint,
"token_endpoint": token_endpoint,
"response_types_supported": ["code"],
"scopes_supported": mcp_server.scopes if mcp_server else [],
"grant_types_supported": ["authorization_code", "refresh_token"],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["client_secret_post"],
# Claude expects a registration endpoint, even if we just fake it
"registration_endpoint": f"{request_base_url}/{mcp_server_name}/register",
"registration_endpoint": f"{request_base_url}/{mcp_server_name}/register" if mcp_server_name else f"{request_base_url}/register",
}
@@ -556,9 +556,33 @@ async def test_oauth_protected_resource_respects_x_forwarded_proto():
from litellm.proxy._experimental.mcp_server.discoverable_endpoints import (
oauth_protected_resource_mcp,
)
from litellm.proxy._experimental.mcp_server.mcp_server_manager import (
global_mcp_server_manager,
)
from litellm.types.mcp import MCPAuth
from litellm.types.mcp_server.mcp_server_manager import MCPServer
from litellm.proxy._types import MCPTransport
from fastapi import Request
except ImportError:
pytest.skip("MCP discoverable endpoints not available")
# Clear registry
global_mcp_server_manager.registry.clear()
# Create mock OAuth2 server
oauth2_server = MCPServer(
server_id="test_oauth_server",
name="test_oauth",
server_name="test_oauth",
alias="test_oauth",
transport=MCPTransport.http,
auth_type=MCPAuth.oauth2,
client_id="test_client_id",
client_secret="test_client_secret",
authorization_url="https://provider.com/oauth/authorize",
token_url="https://provider.com/oauth/token",
scopes=["read", "write"],
)
global_mcp_server_manager.registry[oauth2_server.server_id] = oauth2_server
# Mock request with http base_url but X-Forwarded-Proto: https
mock_request = MagicMock(spec=Request)
@@ -568,13 +592,14 @@ async def test_oauth_protected_resource_respects_x_forwarded_proto():
# Call the endpoint
response = await oauth_protected_resource_mcp(
request=mock_request,
mcp_server_name="test_server",
mcp_server_name="test_oauth",
)
# Verify response uses HTTPS URLs
assert response["authorization_servers"][0].startswith(
"https://litellm.example.com/"
)
assert response["scopes_supported"] == oauth2_server.scopes
@pytest.mark.asyncio
@@ -584,9 +609,33 @@ async def test_oauth_authorization_server_respects_x_forwarded_proto():
from litellm.proxy._experimental.mcp_server.discoverable_endpoints import (
oauth_authorization_server_mcp,
)
from litellm.proxy._experimental.mcp_server.mcp_server_manager import (
global_mcp_server_manager,
)
from litellm.types.mcp import MCPAuth
from litellm.types.mcp_server.mcp_server_manager import MCPServer
from litellm.proxy._types import MCPTransport
from fastapi import Request
except ImportError:
pytest.skip("MCP discoverable endpoints not available")
# Clear registry
global_mcp_server_manager.registry.clear()
# Create mock OAuth2 server
oauth2_server = MCPServer(
server_id="test_oauth_server",
name="test_oauth",
server_name="test_oauth",
alias="test_oauth",
transport=MCPTransport.http,
auth_type=MCPAuth.oauth2,
client_id="test_client_id",
client_secret="test_client_secret",
authorization_url="https://provider.com/oauth/authorize",
token_url="https://provider.com/oauth/token",
scopes=["read", "write"],
)
global_mcp_server_manager.registry[oauth2_server.server_id] = oauth2_server
# Mock request with http base_url but X-Forwarded-Proto: https
mock_request = MagicMock(spec=Request)
@@ -596,7 +645,7 @@ async def test_oauth_authorization_server_respects_x_forwarded_proto():
# Call the endpoint
response = await oauth_authorization_server_mcp(
request=mock_request,
mcp_server_name="test_server",
mcp_server_name="test_oauth",
)
# Verify response uses HTTPS URLs
@@ -604,6 +653,7 @@ async def test_oauth_authorization_server_respects_x_forwarded_proto():
assert response["token_endpoint"].startswith("https://litellm.example.com/")
assert response["registration_endpoint"].startswith("https://litellm.example.com/")
assert response["grant_types_supported"] == ["authorization_code", "refresh_token"]
assert response["scopes_supported"] == oauth2_server.scopes
@pytest.mark.asyncio