"""Tests for auth and httpx_client_factory params on MCPServerSse and MCPServerStreamableHttp.""" from __future__ import annotations from unittest.mock import MagicMock, patch import httpx import pytest from agents.mcp import MCPServerSse, MCPServerStreamableHttp from agents.mcp.server import _create_default_streamable_http_client class TestMCPServerSseAuthAndFactory: """Tests for auth and httpx_client_factory added to MCPServerSseParams.""" @pytest.mark.asyncio async def test_sse_default_no_auth_no_factory(self): """SSE create_streams falls back to the hardened default httpx_client_factory.""" with patch("agents.mcp.server.sse_client") as mock_client: mock_client.return_value = MagicMock() server = MCPServerSse(params={"url": "http://localhost:8000/sse"}) server.create_streams() mock_client.assert_called_once_with( url="http://localhost:8000/sse", headers=None, timeout=5, sse_read_timeout=300, httpx_client_factory=_create_default_streamable_http_client, ) @pytest.mark.asyncio async def test_sse_with_auth(self): """SSE create_streams forwards auth and still applies the hardened default factory.""" auth = httpx.BasicAuth(username="user", password="pass") with patch("agents.mcp.server.sse_client") as mock_client: mock_client.return_value = MagicMock() server = MCPServerSse(params={"url": "http://localhost:8000/sse", "auth": auth}) server.create_streams() mock_client.assert_called_once_with( url="http://localhost:8000/sse", headers=None, timeout=5, sse_read_timeout=300, auth=auth, httpx_client_factory=_create_default_streamable_http_client, ) @pytest.mark.asyncio async def test_sse_with_httpx_client_factory(self): """SSE create_streams forwards a custom httpx_client_factory when provided.""" def custom_factory( headers: dict[str, str] | None = None, timeout: httpx.Timeout | None = None, auth: httpx.Auth | None = None, ) -> httpx.AsyncClient: return httpx.AsyncClient(verify=False) # pragma: no cover with patch("agents.mcp.server.sse_client") as mock_client: mock_client.return_value = MagicMock() server = MCPServerSse( params={ "url": "http://localhost:8000/sse", "httpx_client_factory": custom_factory, } ) server.create_streams() mock_client.assert_called_once_with( url="http://localhost:8000/sse", headers=None, timeout=5, sse_read_timeout=300, httpx_client_factory=custom_factory, ) @pytest.mark.asyncio async def test_sse_with_auth_and_factory(self): """SSE create_streams forwards both auth and httpx_client_factory together.""" auth = httpx.BasicAuth(username="user", password="pass") def custom_factory( headers: dict[str, str] | None = None, timeout: httpx.Timeout | None = None, auth: httpx.Auth | None = None, ) -> httpx.AsyncClient: return httpx.AsyncClient(verify=False) # pragma: no cover with patch("agents.mcp.server.sse_client") as mock_client: mock_client.return_value = MagicMock() server = MCPServerSse( params={ "url": "http://localhost:8000/sse", "headers": {"X-Token": "abc"}, "auth": auth, "httpx_client_factory": custom_factory, } ) server.create_streams() mock_client.assert_called_once_with( url="http://localhost:8000/sse", headers={"X-Token": "abc"}, timeout=5, sse_read_timeout=300, auth=auth, httpx_client_factory=custom_factory, ) class TestMCPServerStreamableHttpAuth: """Tests for the auth parameter added to MCPServerStreamableHttpParams.""" @pytest.mark.asyncio async def test_streamable_http_default_no_auth(self): """StreamableHttp create_streams omits auth when not provided.""" with patch("agents.mcp.server.streamablehttp_client") as mock_client: mock_client.return_value = MagicMock() server = MCPServerStreamableHttp(params={"url": "http://localhost:8000/mcp"}) server.create_streams() mock_client.assert_called_once_with( url="http://localhost:8000/mcp", headers=None, timeout=5, sse_read_timeout=300, terminate_on_close=True, httpx_client_factory=_create_default_streamable_http_client, ) @pytest.mark.asyncio async def test_streamable_http_with_auth(self): """StreamableHttp create_streams forwards the auth parameter when provided.""" auth = httpx.BasicAuth(username="user", password="pass") with patch("agents.mcp.server.streamablehttp_client") as mock_client: mock_client.return_value = MagicMock() server = MCPServerStreamableHttp( params={"url": "http://localhost:8000/mcp", "auth": auth} ) server.create_streams() mock_client.assert_called_once_with( url="http://localhost:8000/mcp", headers=None, timeout=5, sse_read_timeout=300, terminate_on_close=True, httpx_client_factory=_create_default_streamable_http_client, auth=auth, ) @pytest.mark.asyncio async def test_streamable_http_with_auth_and_factory(self): """StreamableHttp create_streams forwards both auth and httpx_client_factory.""" auth = httpx.BasicAuth(username="user", password="pass") def custom_factory( headers: dict[str, str] | None = None, timeout: httpx.Timeout | None = None, auth: httpx.Auth | None = None, ) -> httpx.AsyncClient: return httpx.AsyncClient(verify=False) # pragma: no cover with patch("agents.mcp.server.streamablehttp_client") as mock_client: mock_client.return_value = MagicMock() server = MCPServerStreamableHttp( params={ "url": "http://localhost:8000/mcp", "auth": auth, "httpx_client_factory": custom_factory, } ) server.create_streams() mock_client.assert_called_once_with( url="http://localhost:8000/mcp", headers=None, timeout=5, sse_read_timeout=300, terminate_on_close=True, auth=auth, httpx_client_factory=custom_factory, )