CC-301j · Module 3

Testing MCP Servers

3 min read

MCP servers need three levels of testing. Unit tests verify individual tool handlers — given these parameters, does the handler return the correct result? Integration tests verify the server responds correctly to MCP protocol messages — does tool discovery return the right schemas? Does a tool invocation message produce the right response? End-to-end tests verify the server works with an actual Claude Code client — does Claude discover the tools, invoke them correctly, and handle the results?

Unit testing is straightforward — your handlers are regular async functions that accept parameters and return results. Test them like any other function. Integration testing requires simulating MCP protocol messages. The MCP SDK includes testing utilities that send protocol messages to your server and verify the responses. End-to-end testing is the most valuable but the hardest to automate — it requires a running Claude Code instance that invokes your tools. Start with manual e2e testing during development and add automated integration tests for CI.

import { describe, test, expect } from 'vitest';

// Unit test: test the handler directly
describe('search_customers handler', () => {
  test('returns matching customers', async () => {
    const result = await searchHandler({ query: 'acme' });
    expect(result.content[0].text).toContain('Acme Corp');
  });

  test('returns empty for no matches', async () => {
    const result = await searchHandler({ query: 'nonexistent' });
    const parsed = JSON.parse(result.content[0].text);
    expect(parsed.results).toHaveLength(0);
  });

  test('handles database errors gracefully', async () => {
    mockDb.simulateError('ECONNREFUSED');
    const result = await searchHandler({ query: 'acme' });
    expect(result.isError).toBe(true);
    expect(result.content[0].text).toContain('connection refused');
  });
});