fix(mcp): resolve OAuth endpoints by server_id with IP access checks

The OAuth broker endpoints use get_mcp_server_by_name() to resolve the
MCP server from the URL path. The LiteLLM UI sends server_id (hash) in
the path, causing lookup failure and "authorization url is not set".

Add get_mcp_server_by_id() as a fallback at all five call sites in
discoverable_endpoints.py. Pass client_ip to the ID-based lookup and
enforce _is_server_accessible_from_ip() so that external clients cannot
bypass IP access controls by supplying a server_id directly.

Fixes #30997
This commit is contained in:
KK-MCP 2026-06-22 12:32:38 -07:00
parent cdecfe24e1
commit 88a85f40d8
2 changed files with 16 additions and 7 deletions

View file

@ -612,7 +612,7 @@ async def authorize(
else None
)
if mcp_server is None and lookup_name:
mcp_server = global_mcp_server_manager.get_mcp_server_by_id(lookup_name)
mcp_server = global_mcp_server_manager.get_mcp_server_by_id(lookup_name, client_ip=client_ip)
if mcp_server is None and mcp_server_name is None:
mcp_server = _resolve_oauth2_server_for_root_endpoints(client_ip=client_ip)
if mcp_server is None:
@ -676,7 +676,7 @@ async def token_endpoint(
lookup_name, client_ip=client_ip
)
if mcp_server is None and lookup_name:
mcp_server = global_mcp_server_manager.get_mcp_server_by_id(lookup_name)
mcp_server = global_mcp_server_manager.get_mcp_server_by_id(lookup_name, client_ip=client_ip)
if mcp_server is None and mcp_server_name is None:
mcp_server = _resolve_oauth2_server_for_root_endpoints(client_ip=client_ip)
if mcp_server is None:
@ -997,7 +997,7 @@ async def _build_oauth_protected_resource_response(
)
if mcp_server is None:
mcp_server = global_mcp_server_manager.get_mcp_server_by_id(
mcp_server_name
mcp_server_name, client_ip=client_ip
)
# Build resource URL based on the pattern
@ -1154,7 +1154,7 @@ def _build_oauth_authorization_server_response(
)
if mcp_server is None:
mcp_server = global_mcp_server_manager.get_mcp_server_by_id(
mcp_server_name
mcp_server_name, client_ip=client_ip
)
return {
@ -1324,7 +1324,7 @@ async def register_client(request: Request, mcp_server_name: Optional[str] = Non
)
if mcp_server is None:
mcp_server = global_mcp_server_manager.get_mcp_server_by_id(
mcp_server_name
mcp_server_name, client_ip=client_ip
)
if mcp_server is None:
return dummy_return

View file

@ -4018,13 +4018,22 @@ class MCPServerManager:
)
return IPAddressUtils.is_internal_ip(client_ip, internal_networks)
def get_mcp_server_by_id(self, server_id: str) -> Optional[MCPServer]:
def get_mcp_server_by_id(
self, server_id: str, client_ip: Optional[str] = None
) -> Optional[MCPServer]:
"""
Get the MCP Server from the server id
Get the MCP Server from the server id.
Args:
server_id: The server ID to look up.
client_ip: Optional client IP for access control. When provided,
non-public servers are hidden from external IPs.
"""
registry = self.get_registry()
for server in registry.values():
if server.server_id == server_id:
if not self._is_server_accessible_from_ip(server, client_ip):
return None
return server
return None