Shipping an MCP server without tests is how you get “works on my laptop” demos and broken agents in production. This guide is a practical testing pyramid grounded in the official protocol methods (initialize, tools/list, tools/call) and the MCP Inspector.
Why MCP testing is different
You are not only testing HTTP status codes. You are testing:
- Lifecycle — can a client complete initialization and capability negotiation? (lifecycle)
- Discovery — do tools appear with valid schemas? (tools)
- Execution — do calls succeed, and do failures return actionable
isErrorresults? - Transport — does it work over stdio and/or Streamable HTTP as you intend? (transports)
- Host UX — does Cursor/Claude/VS Code actually surface the tools?
A green container with an empty tools/list is still a failed release.
Testing layers (pyramid)
| Layer | What you prove | How | Cadence |
|---|---|---|---|
| Unit | Handler logic, parsing, authz branches | Mock upstream APIs | Every PR |
| Contract | Schemas stay stable | Snapshot inputSchema / optional outputSchema | Every PR |
| Protocol smoke | Wire correctness | Scripted client: initialize → list → call | Every PR + post-deploy |
| Inspector | Exploratory / edge cases | MCP Inspector | Local + staging |
| Staging host | Real product path | Cursor/Claude/VS Code against staging URL | Before prod promote |
| Load / soak | Concurrency, rate limits, timeouts | Parallel tools/call | Pre-launch, after big changes |
Layer details
1. Unit tests
Treat each tool handler like a pure function:
- Valid inputs → expected structured/text content
- Invalid inputs → validation error (preferably tool execution error with
isError: true, not a crash) - Missing secrets → fail closed with a clear message
- Upstream 4xx/5xx → mapped to actionable errors
Avoid calling real SaaS APIs in unit tests.
2. Contract tests
The model chooses tools from names and descriptions in tools/list. Renaming a tool or making a field required is a breaking change for agents.
- Snapshot the list of tool names
- Snapshot JSON Schema for each tool
- Fail the PR if the snapshot changes without an intentional “breaking” label
See also versioning & rollouts.
3. Protocol smoke (minimum CI gate)
For every deployable artifact:
- Start the server the same way production does
- Complete initialize
- Assert
tools/listcontains expected names - Run one happy-path
tools/call - Run one deliberate bad call — expect
isError: truewith readable text (error handling) - For remote: assert unauthenticated requests fail
4. Official Inspector
Use the MCP Inspector to:
- List tools/resources/prompts
- Invoke tools with structured inputs
- Watch raw JSON-RPC when something feels “stuck”
Pair with the official debugging guide.
5. Staging with a real host
Protocol green ≠ host green. Point a staging client at your staging URL (connect remote, VS Code MCP, Cursor MCP).
Verify:
- Server shows connected
- Tools appear in the UI
- One natural-language task triggers the right tool
- Denial/approval UX still works for sensitive tools (tools interaction model)
Stdio-specific tests
If you ship stdio locally:
- No application logs on stdout — they corrupt JSON-RPC (build server, stdio transport)
- CI should fail if the process prints banners to stdout on startup
Remote / Streamable HTTP tests
- MCP endpoint path (often
/mcp) - POST initialize works
- Auth required when configured
- Origin validation does not block your real clients (transports security)
What not to skip
| Area | Why it bites |
|---|---|
| Auth failure modes | Most production outages |
| Oversized payloads | Latency + context window death |
| Upstream timeouts | Agents retry forever without clear errors |
| Rate-limit behavior | Servers MUST rate limit tools (tools security) |
| Destructive tools | Confirm human approval still gates them |
Example CI gate (conceptual)
pnpm test / pytest
→ start MCP server (same entrypoint as prod)
→ client.initialize()
→ assert tools/list names == snapshot
→ tools/call happy path
→ tools/call invalid args → isError
→ (remote) unauthenticated call → 401
Wire this into CI/CD for MCP.
After deploy
For 30–60 minutes, watch:
- Tool error rate
- Auth failures
- p95 latency
Product: MCPLambda analytics. Process: observability, debugging in production.
Full pre-release checklist
- Unit tests for each tool handler
- Schema/name snapshots reviewed
- Protocol smoke on prod transport
- Inspector session on staging
- One real host (Cursor/Claude/VS Code) smoke
- Secrets not in logs or tool results
- Rate limits and timeouts verified
- Rollback artifact identified
Worked scenario: The PR that would have broken every agent
A developer renames search_docs to docs_search “for consistency.” Unit tests pass because they call the handler directly. Production agents still emit search_docs from cached tool lists and prior chat context.
You add a contract snapshot of tool names and required fields from tools/list. The PR fails CI. You dual-run both names for two weeks with deprecation text in the old tool’s description, then remove the old name.
Separately, you add a protocol smoke job: start the real entrypoint, initialize, assert the snapshot, one happy tools/call, one invalid call expecting isError: true. Stdio jobs fail if anything writes a banner to stdout.
Checklist for this topic
- Unit tests mock upstreams; no live SaaS in PR CI
- Snapshot tool names + inputSchema on every PR
- Protocol smoke uses the production entrypoint
- Invalid inputs return actionable isError results
- Stdio tests fail on stdout pollution
- Staging host smoke before prod promote
Topic-specific failure modes
| Failure | Likely cause | Fix |
|---|---|---|
| CI green, agents broken | Only unit-tested handlers | Add tools/list contract tests |
| Flaky CI | LLM-in-the-loop tests | Deterministic MCP client scripts |
| Inspector works, host does not | Host transport/auth mismatch | Test the same URL/config users use |
| Snapshot always noisy | Unstable descriptions | Snapshot names + required fields only |
Related guides
CI/CD · Versioning · Debugging
Sources
- MCP Inspector
- Debugging
- Tools — 2025-11-25
- Lifecycle — 2025-11-25
- Transports — 2025-11-25
- Build an MCP server
- Connect to remote MCP servers