MCP Server Testing Checklist: Contract to Failure Paths
Test an MCP server as a protocol endpoint and as a capability with real consequences. Verify negotiation, schemas, primitives, transport framing, malformed input, cancellation, authorization, side effects, timeouts, and readback before judging whether a model can use it well.
The MCP server test matrix
| Area | Pass assertion | Failure fixture | Evidence |
|---|---|---|---|
| Initialization | Negotiated protocol and capabilities match implementation. | Unsupported version or undeclared feature | Handshake messages and resulting capability view |
| Discovery | Lists are complete, stable, paginated, and correctly described. | Empty page, changed cursor, duplicate name | Ordered IDs, cursors, notifications |
| Schema | Inputs and structured outputs conform to declared contracts. | Missing field, wrong type, extra field, invalid output | Validation result and protocol error |
| Authorization | Allowed calls work and forbidden calls change no state. | Wrong user, tenant, scope, or resource | Policy decision and before/after readback |
| Execution | Success, business error, and protocol error remain distinct. | Dependency rejection or partial write | Tool result, error class, external state |
| Transport | Framing, cancellation, termination, and reconnect behavior follow the binding. | Truncated message, closed stream, process exit | Wire trace, exit status, cleanup |
| Operational limits | Timeouts, concurrency, rate limits, and payload limits fail safely. | Slow call, duplicate call, oversized input | Timing, attempts, final state, resource use |
| Observability | A run can be explained without logging secrets or raw sensitive content. | Redaction and correlation probe | Trace IDs, bounded metadata, audit event |
Test protocol and primitive contracts independently
The MCP server overview distinguishes prompts, resources, and tools and assigns each a different control model. Test only the primitives the server declares. For resources, verify list, read, templates, subscriptions, MIME types, and not-found behavior where supported. For prompts, validate names, arguments, generated messages, and missing arguments. For tools, validate discovery, input schema, invocation, structured output, and both error mechanisms.
The MCP 2025-06-18 server specification is the contract source for those primitives. Keep protocol fixtures pinned to the version the implementation advertises. A client should not infer support from a method that happened to respond once.
Use the Inspector for exploration and message visibility
The official MCP Inspector guide documents connection controls and dedicated views for resources, prompts, tools, and notifications. It is useful for manual discovery, schema inspection, custom inputs, and edge-case exploration. Convert every important finding into a repeatable automated assertion; a manual session cannot guard future releases by itself.
Exercise the chosen transport and lifecycle
MCP semantics remain consistent across transport bindings, while framing and lifecycle differ. The current draft transport overview describes stdio as newline-delimited messages over a client-launched subprocess and Streamable HTTP as POST requests whose replies are JSON or request-scoped server-sent events.
For stdio, keep protocol messages on standard output and route diagnostic logs elsewhere. Probe early process exit, invalid lines, blocked input, cancellation, and child cleanup. For Streamable HTTP, probe method handling, content types, origin validation, authentication challenges, closed streams, request cancellation, proxy behavior, and bounded reconnect. Test the exact binding you ship, not an in-memory handler alone.
Prove authorization outside protocol success
A valid MCP request is not an authorized action. Build fixtures for allowed and denied principals, tenants, resources, classifications, and scopes. Confirm denial before side effects and verify unchanged external state. Tool descriptions, annotations, and model choices are inputs to policy, not policy decisions.
Use synthetic accounts, files, and records. For mutating tools, verify idempotency keys or duplicate handling, partial-failure cleanup, approval gates, and readback. For read tools and resources, test enumeration leaks, error-message leaks, cache isolation, and whether returned content contains untrusted instructions that the host must treat as data.
Failure paths often missed by happy-path demos
- Declared but broken capability: negotiation advertises a feature whose method fails. Generate tests from advertised capabilities.
- Schema accepts nonsense: JSON types pass while an identifier crosses a business boundary. Add semantic validators and negative cases.
- Tool error reported as success: a business failure appears in normal content without the expected error signal. Assert both protocol and execution errors.
- Cancellation leaves work running: the client stops waiting while the server mutates state. Propagate cancellation and verify cleanup.
- Stdio log corruption: diagnostic text enters the JSON-RPC stream. Separate logging and fail on any non-protocol output.
- Remote origin confusion: an HTTP server accepts requests from an unintended origin or host. Test binding, origin, and authentication policy.
- Inspector-only confidence: manual interaction works but pagination, concurrency, and recovery regress later. Automate the invariant suite.
A release-oriented testing sequence
- Pin the protocol and transport contract. Record advertised version, capabilities, binding, authentication mode, and server lifecycle.
- Generate discovery fixtures. Assert names, descriptions, schemas, pagination, change notifications, and duplicate handling.
- Test each primitive directly. Cover normal, boundary, malformed, missing, unsupported, and oversized requests without a model.
- Add authorization and side-effect fixtures. Use synthetic identities and resources; read state before and after every allowed or denied mutation.
- Inject transport failures. Cancel, close, truncate, delay, restart, and disconnect while checking cleanup and retry behavior.
- Run model-facing scenarios. Only after contracts pass, evaluate tool selection, argument quality, confirmation, result use, and stop behavior.
- Gate releases and preserve traces. Run deterministic checks in isolation, redact sensitive values, and promote incidents into regression fixtures.
Decision summary
Release an MCP server only after protocol contracts, every exposed primitive, the selected transport, authorization boundaries, malformed inputs, and dependency failures pass separately. An Inspector demo is useful evidence, but it is not a substitute for repeatable failure-path tests.
Primary and official sources
- MCP 2025-06-18 server features overview — official primitive and control hierarchy.
- MCP Inspector documentation — official testing and debugging workflow.
- MCP draft transport overview — current binding, framing, metadata, and cancellation model.