diff --git a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py index cf23a22505c..99ca192f3bd 100644 --- a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py +++ b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py @@ -179,7 +179,9 @@ class MCPRequestHandler: elif ( not litellm_api_key and MCPRequestHandler._target_servers_delegate_auth_to_upstream( # noqa: E501 - path=request.url.path, mcp_servers=mcp_servers + path=request.url.path, + mcp_servers=mcp_servers, + client_ip=IPAddressUtils.get_mcp_client_ip(request), ) ): # Operator opted this oauth2 server into upstream-delegated auth @@ -379,7 +381,7 @@ class MCPRequestHandler: @staticmethod def _target_servers_delegate_auth_to_upstream( - path: str, mcp_servers: Optional[List[str]] + path: str, mcp_servers: Optional[List[str]], client_ip: Optional[str] ) -> bool: """ True only when EVERY MCP server the request targets is configured for @@ -409,7 +411,9 @@ class MCPRequestHandler: return False for name in target_names: - server = global_mcp_server_manager.get_mcp_server_by_name(name) + server = global_mcp_server_manager.get_mcp_server_by_name( + name, client_ip=client_ip + ) if server is None or server.auth_type != MCPAuth.oauth2: return False # `is True` is intentional: opt-in must be an explicit boolean diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py b/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py index f719b40fe74..beae2bb61d2 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py @@ -1,7 +1,7 @@ import json import os import sys -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import AsyncMock, MagicMock, call as mock_call, patch import pytest from fastapi.testclient import TestClient @@ -918,7 +918,9 @@ class TestMCPPassthroughColdStartAdmission: await MCPRequestHandler.process_mcp_request(scope) assert exc_info.value.status_code == 401 - mock_mgr.get_mcp_server_by_name.assert_not_called() + mock_mgr.get_mcp_server_by_name.assert_called_once_with( + "passthrough_server", client_ip="" + ) async def test_cold_start_rejects_server_specific_authorization_header(self): from fastapi import HTTPException @@ -1016,7 +1018,7 @@ class TestMCPPassthroughColdStartAdmission: await MCPRequestHandler.process_mcp_request(scope) assert exc_info.value.status_code == 401 - mock_mgr.get_mcp_server_by_name.assert_called_once_with( + assert mock_mgr.get_mcp_server_by_name.call_args_list[-1] == mock_call( "passthrough_server", client_ip="203.0.113.10" ) @@ -1111,7 +1113,7 @@ class TestMCPPassthroughColdStartAdmission: (auth_result, *_rest) = await MCPRequestHandler.process_mcp_request(scope) assert isinstance(auth_result, UserAPIKeyAuth) - mock_mgr.get_mcp_server_by_name.assert_called_once_with( + assert mock_mgr.get_mcp_server_by_name.call_args_list[-1] == mock_call( "passthrough_server", client_ip="" ) @@ -1148,7 +1150,7 @@ class TestMCPPassthroughColdStartAdmission: (auth_result, *_rest) = await MCPRequestHandler.process_mcp_request(scope) assert isinstance(auth_result, UserAPIKeyAuth) - mock_mgr.get_mcp_server_by_name.assert_called_once_with( + assert mock_mgr.get_mcp_server_by_name.call_args_list[-1] == mock_call( "passthrough_server", client_ip="" ) @@ -1996,7 +1998,7 @@ class TestMCPDelegateAuthToUpstream: delegate_auth_to_upstream=True, ) - def lookup_by_name(name): + def lookup_by_name(name, **_kwargs): # Only the *exact* delegated name resolves. Anything else (e.g. # ``delegated_server/extra``) returns None so the bypass fails. if name == "delegated_server": @@ -2059,7 +2061,7 @@ class TestMCPDelegateAuthToUpstream: auth_type=MCPAuth.api_key, ) - def lookup_by_name(name): + def lookup_by_name(name, **_kwargs): return { "delegated_server": delegate_server, "non_delegate_server": non_delegate,