fix(mcp): preserve source_url in GET /v1/mcp/server list responses (#29249)

* 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 <cursoragent@cursor.com>

* 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 <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Shivam Rawat 2026-05-30 12:01:22 -07:00 committed by GitHub
parent f11c12d157
commit ace3c65ab3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 33 additions and 0 deletions

View File

@ -892,6 +892,7 @@ class MCPServerManager:
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 @@ class MCPServerManager:
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,
)

View File

@ -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).

View File

@ -1505,6 +1505,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
@ -1563,6 +1564,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
@ -1622,6 +1624,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)

View File

@ -2888,6 +2888,31 @@ class TestMCPServerTimestamps:
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

View File

@ -857,6 +857,7 @@ class TestSigV4BuildFromTable:
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 @@ class TestSigV4BuildFromTable:
table_record.byok_api_key_help_url = None
table_record.oauth2_flow = None
table_record.instructions = None
table_record.source_url = None
manager = MCPServerManager()