Add ergonomic Client class for testing MCP servers (#1870)

This commit is contained in:
Felix Weinberger 2026-01-16 15:49:26 +00:00 • committed by GitHub
commit df039bf97c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 1298 additions and 526 deletions

View file

@ -1,10 +1,8 @@
import pytest
from pydantic import AnyUrl
from mcp import Client
from mcp.server.fastmcp import FastMCP
from mcp.shared.memory import (
create_connected_server_and_client_session as client_session,
)
from mcp.types import (
ListResourceTemplatesResult,
TextResourceContents,
@ -78,10 +76,7 @@ async def test_resource_template_client_interaction():
def get_user_profile(user_id: str) -> str:
return f"Profile for user {user_id}"
async with client_session(mcp._mcp_server) as session:
# Initialize the session
await session.initialize()
async with Client(mcp) as session:
# List available resources
resources = await session.list_resource_templates()
assert isinstance(resources, ListResourceTemplatesResult)

View file

@ -3,13 +3,10 @@ import base64
import pytest
from pydantic import AnyUrl
from mcp import types
from mcp import Client, types
from mcp.server.fastmcp import FastMCP
from mcp.server.lowlevel import Server
from mcp.server.lowlevel.helper_types import ReadResourceContents
from mcp.shared.memory import (
create_connected_server_and_client_session as client_session,
)
pytestmark = pytest.mark.anyio
@ -33,7 +30,7 @@ async def test_fastmcp_resource_mime_type():
return image_bytes
# Test that resources are listed with correct mime type
async with client_session(mcp._mcp_server) as client:
async with Client(mcp) as client:
# List resources and verify mime types
resources = await client.list_resources()
assert resources.resources is not None
@ -91,7 +88,7 @@ async def test_lowlevel_resource_mime_type():
raise Exception(f"Resource not found: {uri}") # pragma: no cover
# Test that resources are listed with correct mime type
async with client_session(server) as client:
async with Client(server) as client:
# List resources and verify mime types
resources = await client.list_resources()
assert resources.resources is not None

View file

@ -12,12 +12,9 @@ These tests verify the fix works end-to-end through the JSON-RPC protocol.
import pytest
from mcp import types
from mcp import Client, types
from mcp.server.lowlevel import Server
from mcp.server.lowlevel.helper_types import ReadResourceContents
from mcp.shared.memory import (
create_connected_server_and_client_session as client_session,
)
pytestmark = pytest.mark.anyio
@ -48,7 +45,7 @@ async def test_relative_uri_roundtrip():
)
]
async with client_session(server) as client:
async with Client(server) as client:
# List should return the exact URIs we specified
resources = await client.list_resources()
uri_map = {r.uri: r for r in resources.resources}
@ -83,7 +80,7 @@ async def test_custom_scheme_uri_roundtrip():
async def read_resource(uri: str):
return [ReadResourceContents(content="data", mime_type="text/plain")]
async with client_session(server) as client:
async with Client(server) as client:
resources = await client.list_resources()
uri_map = {r.uri: r for r in resources.resources}

View file

@ -7,10 +7,8 @@ with parameters like 'text/html;profile=mcp-app' which are valid per RFC 2045.
import pytest
from pydantic import AnyUrl
from mcp import Client
from mcp.server.fastmcp import FastMCP
from mcp.shared.memory import (
create_connected_server_and_client_session as client_session,
)
pytestmark = pytest.mark.anyio
@ -63,7 +61,7 @@ async def test_mime_type_preserved_in_read_resource():
def my_widget() -> str:
return "<html><body>Hello MCP-UI</body></html>"
async with client_session(mcp._mcp_server) as client:
async with Client(mcp) as client:
# Read the resource
result = await client.read_resource(AnyUrl("ui://my-widget"))
assert len(result.contents) == 1

View file

@ -2,8 +2,8 @@ import anyio
import pytest
from pydantic import AnyUrl
from mcp import Client
from mcp.server.fastmcp import FastMCP
from mcp.shared.memory import create_connected_server_and_client_session as create_session
@pytest.mark.anyio
@ -30,7 +30,7 @@ async def test_messages_are_executed_concurrently_tools():
call_order.append("trigger_end")
return "slow"
async with create_session(server._mcp_server) as client_session:
async with Client(server) as client_session:
# First tool will wait on event, second will set it
async with anyio.create_task_group() as tg:
# Start the tool first (it will wait on event)
@ -70,7 +70,7 @@ async def test_messages_are_executed_concurrently_tools_and_resources():
call_order.append("resource_end")
return "slow"
async with create_session(server._mcp_server) as client_session:
async with Client(server) as client_session:
# First tool will wait on event, second will set it
async with anyio.create_task_group() as tg:
# Start the tool first (it will wait on event)