Testing an MCP Server Against a Real Agent Client
Unit tests on handlers are not enough. MCP bugs often live in transport framing, initialize negotiation, or host-specific config.
Search across all documentation pages
Unit tests on handlers are not enough. MCP bugs often live in transport framing, initialize negotiation, or host-specific config.
You need a layered test strategy: protocol client harness, host install check, and optional full agent loop.
Build an automated stdio (or HTTP) MCP client that initializes, lists tools, and calls golden paths; then install the same server in a real host (VS Code, Claude Desktop, or an Agents SDK MCP connector) and run a short scripted task that must invoke your tools correctly.
# tests/test_mcp_smoke.py - verify client APIs at build
import asyncio
import sys
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
SERVER = [
sys.executable,
"/ABSOLUTE/PATH/TO/server.py",
]
async def smoke() -> None:
params = StdioServerParameters(command=SERVER[0], args=SERVER[1:])
async with stdio_client(params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
listed = await session.list_tools()
names = sorted(t.name for t in listed.tools)
assert "echo" in names, names
ok = await session.call_tool("echo", {"message": "pong"})
texts = [c.text for c in ok.content if hasattr(c, "text")]
assert any("pong" in t for t in texts), texts
bad = await session.call_tool("echo", {}) # missing args
# Prefer structured isError when available; at least ensure no crash
assert bad is not None
def test_mcp_smoke() -> None:
asyncio.run(smoke())pytest tests/test_mcp_smoke.py -qReal host checklist (manual or documented runbook):
| Layer | What it proves | What it misses |
|---|---|---|
| Unit tests | Business logic | Protocol, schemas, host UX |
| MCP client harness | Wire protocol + handlers | Host UI, OAuth browser, model routing |
| Real host / agent | End-to-end usability | Full matrix of every host |
Ship blockers usually need the middle layer green. Product confidence needs the third.
Snapshot tool names and required JSON Schema properties in CI.
Unexpected renames break agents that hardcode tool names in prompts or evals.
Models will send wrong types. Your server should return a controlled error content path, not segfault.
Harness tests with empty objects, wrong types, and boundary values.
Real upstream APIs need retries and recorded fixtures.
Default CI should use fakes so MCP protocol tests stay deterministic.
Add harness cases for:
Mcp-Session-Id after initializeMCP-Protocol-Version headerWhen using OpenAI Agents SDK, Claude Agent SDK, or similar with native MCP, write one eval case: user goal only solvable via your tool.
Assert the transcript contains a tool call to your server, not just a plausible hallucination.
isError - success HTTP/RPC with error payload.| Approach | When it wins | When it loses |
|---|---|---|
| Client harness (this recipe) | Fast, automatable protocol truth | Weak UX signal |
| Host-only manual QA | Early prototype | Regressions return |
| Contract tests from OpenAPI | REST backends | Skips MCP framing |
| Full multi-agent eval suite | Production agents | Expensive for tiny servers |
Interactive inspectors are great for debugging. Keep a headless harness for CI so merges stay honest.
Mock business dependencies, not the transport, for smoke tests. You want real JSON-RPC bytes.
Mirror the tool smoke: list, then read/get with fixtures. Assert mime types and template text.
Split tests: unauthenticated endpoints return 401 with WWW-Authenticate; authenticated path uses a test token from a local IdP or stub verifier.
Yes for HTTP. For stdio, per-test processes are slower but isolate state. Prefer isolation unless startup is heavy.
In the harness, ensure the client still parses messages; optionally wrap the server to fail if non-JSON lines appear on stdout.
Yes for stable formatters. Avoid golden files for timestamps and UUIDs without scrubbers.
When tool choice is non-obvious or descriptions are subtle. Protocol green does not prove the model will call you.
Related: Building & Deploying MCP Basics
Related: Building Your First MCP Server in TypeScript or Python
Stack versions: Pins from the category manifest (verify at build): OpenRouter (~315+ models, July 2026 pricing/fees); LangGraph 1.0+; CrewAI 1.14+; Microsoft Agent Framework 1.0; Vercel AI SDK 6; Pydantic AI (latest); LlamaIndex (latest); OpenAI Agents SDK (latest + MCP); MCP (Linux Foundation governance); A2A (HTTP+SSE+JSON-RPC 2.0); Solana
@solana/web3.js+@solana/spl-token.
Reviewed by Chris St. John·Last updated Jul 16, 2026