From 1b782e8313c71ffd9a7f69cbc7fa1b9ba7b5e277 Mon Sep 17 00:00:00 2001 From: shivam Date: Thu, 28 May 2026 19:22:02 -0700 Subject: [PATCH 1/2] fix(mcp): preserve source_url in GET /v1/mcp/server list responses The list endpoint builds responses from the in-memory registry, but source_url was dropped during the DB-to-registry roundtrip even though GET /v1/mcp/server/{id} returned it correctly from the database. Co-authored-by: Cursor --- .../mcp_server/mcp_server_manager.py | 2 ++ .../types/mcp_server/mcp_server_manager.py | 1 + .../mcp_server/test_mcp_server_manager.py | 25 +++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py index d0e9ad7b2a4d..f35aa30a7c91 100644 --- a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py +++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py @@ -892,6 +892,7 @@ async def build_mcp_server_from_table( is_byok=bool(getattr(mcp_server, "is_byok", False)), byok_description=getattr(mcp_server, "byok_description", None) or [], byok_api_key_help_url=getattr(mcp_server, "byok_api_key_help_url", None), + source_url=getattr(mcp_server, "source_url", None), # AWS SigV4 fields aws_access_key_id=aws_creds.get("aws_access_key_id"), aws_secret_access_key=aws_creds.get("aws_secret_access_key"), @@ -3750,6 +3751,7 @@ def _build_mcp_server_table(self, server: MCPServer) -> LiteLLM_MCPServerTable: is_byok=server.is_byok, byok_description=server.byok_description, byok_api_key_help_url=server.byok_api_key_help_url, + source_url=server.source_url, instructions=server.instructions, ) diff --git a/litellm/types/mcp_server/mcp_server_manager.py b/litellm/types/mcp_server/mcp_server_manager.py index 776c7fa67a6e..13e325838dc7 100644 --- a/litellm/types/mcp_server/mcp_server_manager.py +++ b/litellm/types/mcp_server/mcp_server_manager.py @@ -77,6 +77,7 @@ class MCPServer(BaseModel): is_byok: bool = False byok_description: List[str] = [] byok_api_key_help_url: Optional[str] = None + source_url: Optional[str] = None created_at: Optional[datetime] = None updated_at: Optional[datetime] = None # OAuth2 flow type. Defaults to None (interactive / authorization_code). diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py index d7078412a447..2db9845c765b 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py @@ -2888,6 +2888,31 @@ async def test_round_trip_timestamps_preserved(self): assert rebuilt_table.created_at == created assert rebuilt_table.updated_at == updated + @pytest.mark.asyncio + async def test_round_trip_source_url_preserved(self): + """source_url survives the full round-trip: LiteLLM_MCPServerTable -> MCPServer -> LiteLLM_MCPServerTable. + + Regression test: the list endpoint (GET /v1/mcp/server) builds its + response from the registry via this round-trip, so a dropped field + here surfaces as a null source_url in the list response even though + the value is stored in the DB. + """ + manager = MCPServerManager() + + table_record = LiteLLM_MCPServerTable( + server_id="src-url-server", + server_name="src_url_server", + url="https://example.com/mcp", + transport=MCPTransport.http, + source_url="https://github.com/org/mcp-server", + ) + + mcp_server = await manager.build_mcp_server_from_table(table_record) + assert mcp_server.source_url == "https://github.com/org/mcp-server" + + rebuilt_table = manager._build_mcp_server_table(mcp_server) + assert rebuilt_table.source_url == "https://github.com/org/mcp-server" + class TestInternalDelegatePkceWarningLog: @pytest.mark.asyncio From 63207944b8510ba657f50ef6fa5c37ace830869b Mon Sep 17 00:00:00 2001 From: shivam Date: Sat, 30 May 2026 10:12:00 -0700 Subject: [PATCH 2/2] fix(tests/mcp): set source_url on MagicMock table records MagicMock auto-creates source_url as a mock object, which fails MCPServer Pydantic validation after source_url was wired through build_mcp_server_from_table. Co-authored-by: Cursor --- tests/mcp_tests/test_mcp_server.py | 3 +++ .../proxy/_experimental/mcp_server/test_mcp_sigv4_auth.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/tests/mcp_tests/test_mcp_server.py b/tests/mcp_tests/test_mcp_server.py index c20fb09eeba7..7687034a7f58 100644 --- a/tests/mcp_tests/test_mcp_server.py +++ b/tests/mcp_tests/test_mcp_server.py @@ -1500,6 +1500,7 @@ async def test_add_update_server_with_alias(): mock_mcp_server.created_at = None mock_mcp_server.updated_at = None mock_mcp_server.instructions = None + mock_mcp_server.source_url = None mock_mcp_server.approval_status = "active" # Add server to manager @@ -1558,6 +1559,7 @@ async def test_add_update_server_without_alias(): mock_mcp_server.created_at = None mock_mcp_server.updated_at = None mock_mcp_server.instructions = None + mock_mcp_server.source_url = None mock_mcp_server.approval_status = "active" # Add server to manager @@ -1617,6 +1619,7 @@ async def test_add_update_server_fallback_to_server_id(): mock_mcp_server.created_at = None mock_mcp_server.updated_at = None mock_mcp_server.instructions = None + mock_mcp_server.source_url = None mock_mcp_server.approval_status = "active" # Add server to manager await test_manager.add_server(mock_mcp_server) diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_sigv4_auth.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_sigv4_auth.py index 32b988ddb22a..0c2a8bb80872 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_sigv4_auth.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_sigv4_auth.py @@ -857,6 +857,7 @@ async def test_build_mcp_server_from_table_with_sigv4_credentials(self): table_record.byok_api_key_help_url = None table_record.oauth2_flow = None table_record.instructions = None + table_record.source_url = None manager = MCPServerManager() @@ -915,6 +916,7 @@ async def test_build_mcp_server_from_table_without_sigv4_credentials(self): table_record.byok_api_key_help_url = None table_record.oauth2_flow = None table_record.instructions = None + table_record.source_url = None manager = MCPServerManager()