The consumer of your MCP server is non-deterministic. The same tool call with the same description fires sometimes and not others. The same parameter shape produces different argument values on different runs. Traditional API testing -- pin the request, assert the response, ship -- catches none of the failure modes that actually break MCP servers in production.

This guide covers the three test layers that earn their keep and the harness pattern that makes end-to-end testing tolerable instead of accepting that "non-deterministic" means "untested." The full chapter is Chapter 8 of MCP in Production.

Layer 1: unit tests against the handler

Test your handlers like any other code. Given an input shape, assert the output shape. Skip the MCP transport; call the handler function directly. Fast, deterministic, catches the bugs you would catch in any other Node project.

What it does not catch: anything about the model's interaction with the tool. The handler works in isolation; the model still picks the wrong tool, passes the wrong argument shape, or interprets the response in a way you did not expect.

Layer 2: integration tests against a real MCP client

Spin up your server, connect a real client (the SDK's test client, or a scripted Claude Code session), call tools/list, then tools/call with a known argument shape. Assert the protocol-level behavior: correct tool list, correct parameter validation, correct response format.

This catches the layer of bugs that unit tests miss: schema mistakes that look fine in the handler but break the JSON-RPC contract, error responses that violate the protocol, capability declarations that don't match what you actually serve.

What it does not catch: tool selection. The integration test passes a known tool name and a known argument; the model in production picks the tool itself.

Layer 3: end-to-end against a real model

The expensive layer. Set up a real Claude (or other LLM) session with your server attached. Give it a natural-language prompt. Score the outcome.

The naive shape is brittle:

// Tuesday: passes // Thursday: fails (model picked sibling tool) test("creates a customer when asked", async () => { const result = await claudeChat("Make a customer for jeff@example.com"); expect(result.toolCalls).toEqual([ { name: "create_customer", args: { email: "jeff@example.com" } } ]); });

The model picked a sibling tool. The test failed. The server is fine. You spend twenty minutes debugging before realizing nothing in the server changed.

The harness pattern

Instead of asserting the exact tool call, assert the eventual state. The model can take three different paths to the same outcome; the test only cares whether the outcome happened.

test("creates a customer when asked", async () => { await claudeChat("Make a customer for jeff@example.com"); // Did a customer with that email end up in the upstream system? const customer = await fixture.findCustomer("jeff@example.com"); expect(customer).toBeDefined(); });

The harness owns the fixture state, runs the prompt, and asserts the post-condition. Whether the model fired create_customer, create_user, or some other path is irrelevant; the test passes if the system reached the right state.

This makes the test resilient to model drift, tool renames, and the probabilistic-tool-selection problem. It also reframes "testing" as "evaluating" -- the right mental model for an LLM-driven consumer.

What good E2E suites look like

The 88-test grading suite

The Yaw Labs 88-test grading rubric for MCP servers is a worked example of this -- the test set that surfaces schema, error-handling, and composition problems across servers. Reading it side-by-side with the testing chapter is the fastest way to internalize the harness pattern.

Want the full chapter?

MCP in Production Chapter 8 is the testing chapter. It covers the three layers in depth, the harness pattern with full code, eval-vs-test framing, scoring patterns, and the CI integration that catches a regression without paying the E2E cost on every push.

MCP in Production

The MCP server book. Twelve chapters from shipping fourteen @yawlabs/* servers. PDF + EPUB. Free updates as the spec moves. $39 one-time, secure checkout.

Read more & buy $39

Published by Yaw Labs.

Related