Lesson 13 / 31

Testing and Inspecting MCP Servers

Verify behaviour with the Inspector, scripted clients and unit tests.

Test the contract, not the model

An MCP server is deterministic code with a published contract, so test it without any model. (1) Unit-test the underlying functions. (2) Protocol-test with a scripted client, like the raw JSON-RPC session shown in this course, asserting the list of tools, their schemas, results and error codes. (3) Use the official MCP Inspector, an interactive tool for connecting to a server and calling its tools, resources and prompts by hand. (4) Add snapshot tests of tools/list so any change to names or schemas is noticed in review, since those are your public API. (5) Finally run a few end-to-end evaluations with a real model to check that it picks the right tool from your descriptions.

A protocol test (illustrative)

Uses a helper like the call function from the raw session above. Not run as a test here.

def test_tools_contract():
    tools = call(2, "tools/list")["result"]["tools"]
    assert [t["name"] for t in tools] == ["get_order_status"]
    assert tools[0]["inputSchema"]["required"] == ["order_id"]

def test_invalid_order_id_is_a_clear_error():
    r = call(3, "tools/call", {"name": "get_order_status", "arguments": {"order_id": "12"}})
    assert "6 digits" in r["result"]["content"][0]["text"]

def test_unknown_method_is_a_protocol_error():
    assert call(4, "no/such/method")["error"]["code"] == -32601

Quick check: Why snapshot tools/list in tests?

  • To reduce server memory
  • Names and schemas are your public API; changes should be noticed
  • To avoid authentication
  • To train the model
Answer

Names and schemas are your public API; changes should be noticed — Accidental contract changes can silently break clients and prompts.