[ ABORT TO HUD ]
SEQ. 1
SEQ. 2
SEQ. 3

Integration Testing

🔍 Testing & Debugging12 min110 BASE XP

Automated Testing for MCP Servers

Manual testing with the Inspector is great for development, but production servers need automated tests that run in CI/CD.

Test Architecture

// test/server.test.ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { describe, it, expect, beforeAll, afterAll } from "vitest";

let client: Client;

beforeAll(async () => {
  const transport = new StdioClientTransport({
    command: "tsx",
    args: ["src/index.ts"],
    env: { NOTES_DIR: "./test/fixtures/notes" }
  });
  client = new Client({ name: "test-runner", version: "1.0.0" });
  await client.connect(transport);
});

afterAll(async () => {
  await client.close();
});

describe("Tool: search_notes", () => {
  it("finds notes matching a keyword", async () => {
    const result = await client.callTool("search_notes", {
      query: "meeting",
      maxResults: 5
    });
    expect(result.isError).toBeFalsy();
    expect(result.content[0].type).toBe("text");
    expect(result.content[0].text).toContain("meeting");
  });

  it("returns empty message for no matches", async () => {
    const result = await client.callTool("search_notes", {
      query: "xyznonexistent123"
    });
    expect(result.isError).toBeFalsy();
    expect(result.content[0].text).toContain("No notes found");
  });

  it("handles missing arguments gracefully", async () => {
    try {
      await client.callTool("search_notes", {});
    } catch (e: any) {
      expect(e.message).toBeDefined();
    }
  });
});

describe("Capabilities", () => {
  it("exposes expected tools", async () => {
    const { tools } = await client.listTools();
    const toolNames = tools.map(t => t.name);
    expect(toolNames).toContain("search_notes");
    expect(toolNames).toContain("create_note");
  });

  it("exposes resources", async () => {
    const { resources } = await client.listResources();
    expect(resources.length).toBeGreaterThan(0);
  });

  it("exposes prompts", async () => {
    const { prompts } = await client.listPrompts();
    expect(prompts.length).toBeGreaterThan(0);
  });
});

Testing Strategy

Test TypeWhat It ValidatesSpeedWhen to Run
Unit TestsTool handler functions in isolationFast (~1s)Every commit
Integration TestsFull client → server round-tripMedium (~5s)Every PR
Protocol TestsJSON-RPC message format complianceMedium (~3s)Every PR
Smoke TestsServer starts and responds to initFast (~2s)Every deploy
💡 Key Insight: The most valuable test for an MCP server is the integration test using the actual Client SDK. It validates the entire stack: transport, protocol, capability negotiation, and tool execution in one test.
SYNAPSE VERIFICATION
QUERY 1 // 3
What is the most valuable test type for MCP servers?
Unit tests of individual functions
Integration tests using the actual Client SDK for full round-trip validation
Manual testing only
Load tests
Watch: 139x Rust Speedup
Integration Testing | Testing & Debugging — MCP Academy