In-memory tests exercise the full protocol path in milliseconds, without a host or a model.
The Inspector is for exploring; tests stop regressions. The pattern is simple: create a Client with your server object, call tools, and assert on the results. Nothing is launched and no port is opened, yet the request still goes through the SDK's full protocol handling, including schema validation.
Setup details:
- Client code is async, so tests need an async test runner. The SDK's docs use the
anyiopytest plugin: mark tests with@pytest.mark.anyioand define ananyio_backendfixture returning"asyncio". - A fixture (pytest's way of preparing a value for a test) yields a connected client, so each test gets a fresh connection.
- Pass
raise_exceptions=Truein tests. Normally a crash outside your tool body is turned into a generic "Internal server error" so details never leak to a remote caller; in a test you want the real error message.
What to assert:
- the tool list and schemas (catch accidental renames, which break hosts and prompts),
structured_contentfor normal calls,is_errorand the message for bad inputs,- resources and prompts render as expected.
Note that Client(mcp) speaks the newest protocol version by default. If a test relies on old-style behaviour, pass mode="legacy".
Example
# test_notes.py (pip install pytest anyio)
import pytest
from mcp import Client
from notes_server import mcp
@pytest.fixture
def anyio_backend():
return "asyncio"
@pytest.fixture
async def client():
async with Client(mcp, raise_exceptions=True) as c:
yield c
@pytest.mark.anyio
async def test_tools_are_listed(client: Client):
names = {t.name for t in (await client.list_tools()).tools}
assert names == {"list_notes"}
@pytest.mark.anyio
async def test_list_notes(client: Client):
result = await client.call_tool("list_notes", {})
assert not result.is_error
assert result.structured_content == {"result": ["groceries", "ideas"]}Remember
- Client(mcp) tests the full protocol path in memory
- Use @pytest.mark.anyio with an anyio_backend fixture
- raise_exceptions=True shows real errors in tests
- Assert on tool names and schemas to catch breaking renames