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.
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
- ▸A modern testing framework installed (like Vitest or Jest).
- ▸
@modelcontextprotocol/sdkinstalled as a dependency. - ▸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:
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_PARAMSerrors 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-RPCErrors: 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
How to Build a Custom MCP Server from Scratch
Learn how to create your own MCP server using the TypeScript or Python SDK. Expose custom tools and resources for AI agents to use.
Dev ToolsHow to Use the Puppeteer MCP Server for Web Scraping & Automation
Automate web browsers using the Puppeteer MCP server. Navigate websites, take screenshots, extract data, and automate web interactions through AI.
Dev ToolsHow to Use the Git MCP Server for Version Control Operations
Manage local Git repositories through AI agents. Clone repos, create branches, make commits, and view diffs using the Git MCP server.