Dev Tools·
advanced
·13 min read·Jul 9, 2026

Testing and Mocking: Unit Testing and Mocking JSON-RPC Payloads for MCP

Write bulletproof unit tests for your MCP servers by mocking Stdio transports and validating JSON-RPC message exchanges.

testingJestVitestmockingJSON-RPCunit-test

Testing and Mocking: Unit Testing & JSON-RPC

Building reliable MCP servers requires writing comprehensive test suites. Because MCP servers communicate asynchronously via JSON-RPC payloads over standard input/output streams or network sockets, you must mock transport interfaces and validate payload formats accurately to ensure deterministic testing.

Prerequisites

  1. A modern testing framework installed (like Vitest or Jest).
  2. @modelcontextprotocol/sdk installed as a dependency.
  3. TypeScript configured for your testing environment.

Mocking Stdio Transports with Vitest

Rather than actually spawning child processes and piping stdin/stdout during unit tests, you can mock the transport layer entirely and pass raw JSON-RPC objects directly to the MCP Server instance.

Here is a comprehensive unit-test setup using Vitest to test MCP tool registration and response formats:

typescript
import { describe, it, expect } from 'vitest';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';

describe('MCP Server Unit Tests', () => {
  it('should return correct compound interest calculations', async () => {
    // 1. Initialize Server
    const testServer = new Server(
      { name: 'test-server', version: '1.0.0' },
      { capabilities: { tools: {} } }
    );

    // 2. Register Tool Handlers
    testServer.setRequestHandler(CallToolRequestSchema, async (request) => {
      if (request.params.name === 'calculate') {
        return { content: [{ type: 'text', text: '42' }] };
      }
      throw new Error('Tool not found');
    });

    // 3. Mock the JSON-RPC Request Payload
    const mockRequest = {
      jsonrpc: '2.0' as const,
      id: 1,
      method: 'tools/call' as const,
      params: {
        name: 'calculate',
        arguments: {}
      }
    };

    // 4. Execute and Assert
    const response = await testServer.handleRequest(mockRequest);
    expect(response).toHaveProperty('result');
    expect(response.result.content[0].text).toBe('42');
  });
});

Best Practices

  • Validate Input Schemas: Your tests should pass invalid arguments (e.g., strings instead of numbers) to ensure your Zod schemas correctly throw INVALID_PARAMS errors before your tool logic executes.
  • Mock External APIs: If your tool makes external HTTP calls (e.g., fetching GitHub data), use libraries like msw (Mock Service Worker) to intercept those requests rather than hitting production endpoints during CI/CD.

Troubleshooting

  • Invalid JSON-RPC Errors: Ensure your mock requests strictly include "jsonrpc": "2.0" and a unique "id". The MCP SDK will reject payloads that don't adhere to the exact RPC specification.
  • Hanging Tests: If your test suite hangs, ensure that any streams or transports initialized during tests are explicitly closed or disconnected in an afterAll() cleanup block.

Related Guides