fix mcp list result parsing

This commit is contained in:
kigland 2026-06-07 10:39:16 +08:00
parent 3448bf79f8
commit 4df4c9cf00
2 changed files with 56 additions and 3 deletions

View file

@ -18,6 +18,7 @@ from typing import (
Union,
)
import httpx
import mcp.types as types
from mcp import ClientSession, ReadResourceResult, Resource, StdioServerParameters
from mcp.client.sse import sse_client
from mcp.client.stdio import stdio_client
@ -41,7 +42,7 @@ from mcp.types import (
TextContent,
)
from mcp.types import Tool as MCPTool
from pydantic import AnyUrl
from pydantic import AnyUrl, Field
from litellm._logging import verbose_logger
from litellm.constants import MCP_CLIENT_TIMEOUT, MCP_NPM_CACHE_DIR
from litellm.llms.custom_httpx.http_handler import get_ssl_configuration
@ -81,6 +82,14 @@ def _first_non_cancelled_cause(exc: BaseException) -> Optional[BaseException]:
return None
class _LenientListPromptsResult(types.ListPromptsResult):
prompts: List[Prompt] = Field(default_factory=list)
class _LenientListResourcesResult(types.ListResourcesResult):
resources: List[Resource] = Field(default_factory=list)
TSessionResult = TypeVar("TSessionResult")
@ -628,7 +637,10 @@ class MCPClient:
)
async def _list_prompts_operation(session: ClientSession):
return await session.list_prompts()
return await session.send_request(
types.ClientRequest(types.ListPromptsRequest(params=None)),
_LenientListPromptsResult,
)
try:
result = await self.run_with_session(_list_prompts_operation)
@ -713,7 +725,10 @@ class MCPClient:
)
async def _list_resources_operation(session: ClientSession):
return await session.list_resources()
return await session.send_request(
types.ClientRequest(types.ListResourcesRequest(params=None)),
_LenientListResourcesResult,
)
try:
result = await self.run_with_session(_list_resources_operation)

View file

@ -542,6 +542,44 @@ class TestExecuteSessionOperationSurfacesTransportError:
result = await client._execute_session_operation(transport_ctx, _op)
assert result == "done"
@pytest.mark.asyncio
async def test_list_resources_tolerates_missing_resources_field(self):
client = MCPClient(server_url="http://example.com/mcp", transport_type="http")
session = AsyncMock()
async def send_request(_request, result_type):
assert result_type is mcp_client_module._LenientListResourcesResult
return result_type.model_validate({})
session.send_request = AsyncMock(side_effect=send_request)
async def run_with_session(operation):
return await operation(session)
client.run_with_session = AsyncMock(side_effect=run_with_session)
assert await client.list_resources() == []
session.send_request.assert_awaited_once()
@pytest.mark.asyncio
async def test_list_prompts_tolerates_missing_prompts_field(self):
client = MCPClient(server_url="http://example.com/mcp", transport_type="http")
session = AsyncMock()
async def send_request(_request, result_type):
assert result_type is mcp_client_module._LenientListPromptsResult
return result_type.model_validate({})
session.send_request = AsyncMock(side_effect=send_request)
async def run_with_session(operation):
return await operation(session)
client.run_with_session = AsyncMock(side_effect=run_with_session)
assert await client.list_prompts() == []
session.send_request.assert_awaited_once()
if __name__ == "__main__":
pytest.main([__file__])