Mutation Server Protocol Specification
The `mutation-server-protocol` package defines the Mutation Server Protocol (MSP), a standardized, language-agnostic specification based on JSON-RPC 2.0. Its purpose is to enable seamless communication between Integrated Development Environments (IDEs) or other tools and mutation testing frameworks. Inspired by the Language Server Protocol, MSP establishes a unified method for initiating mutation tests, reporting their progress, and exchanging structured data like mutation locations and results. The current stable version is 0.4.2, with recent minor updates in March 2026, indicating active maintenance within the Stryker Mutator monorepo. Key differentiators include its explicit focus on mutation testing, support for both Standard Input/Output (stdio) and TCP/IP Socket transport modes, and detailed message formats for operations such as mutant discovery and execution. It emphasizes 1-based indexing for positions and locations, aligning with common text editor conventions.
Common errors
-
Content-Length header missing or invalid
cause The incoming JSON-RPC message is not correctly framed with the required `Content-Length` header followed by `\r\n\r\n` before the JSON payload.fixEnsure all messages sent over `stdio` or `socket` transports adhere to the base protocol's framing: `Content-Length: <length>\r\n\r\n{json_payload}`. -
Error: listen EADDRINUSE: address already in use :::<port>
cause When running the server in `socket` mode, the specified `--port` is already occupied by another process.fixChoose an available port number, or ensure no other application is using the intended port before starting the mutation server. -
TypeError: Cannot read properties of undefined (reading 'fileName') for FileRange
cause A `FileRange` object passed to `discover` or `mutationTest` (e.g., in the `files` array) is missing the required `fileName` property, or the `files` array itself is not properly structured.fixEnsure `FileRange` objects always include a `fileName` string. Validate that the `files` array matches the `FileRange[]` type definition.
Warnings
- breaking The semantics for `position` and `location` fields were clarified and aligned with the `mutation-testing-report-schema` in version `0.3.0`. Positions are now 1-based, with `start` being inclusive and `end` exclusive.
- breaking The response objects for `discover` and `mutationTest` methods were extended in version `0.2.0` to include a `files` property. Clients built against `0.1.x` might fail to parse the new response structure.
- gotcha As of `0.4.0`, mutation servers are required to support both `stdio` and `socket` transport modes and adhere to a specific command-line argument format for `--port` and `--address` when using `socket`.
- gotcha When using `stdio` transport, the protocol strictly reserves standard output (`stdout`) for JSON-RPC messages only. Any logging or debugging information written to `stdout` will corrupt the message stream.
Install
-
npm install mutation-server-protocol -
yarn add mutation-server-protocol -
pnpm add mutation-server-protocol
Imports
- MutationServer
import { MutationServer } from 'mutation-server-protocol';import type { MutationServer } from 'mutation-server-protocol'; - DiscoverParams, MutationTestParams
const { DiscoverParams } = require('mutation-server-protocol');import type { DiscoverParams, MutationTestParams } from 'mutation-server-protocol'; - FileRange
import * as protocol from 'mutation-server-protocol';
import type { FileRange } from 'mutation-server-protocol';
Quickstart
import type {
MutationServer,
DiscoverParams,
MutationTestParams,
MutationTestResult,
FileRange,
Mutant,
Location,
} from 'mutation-server-protocol';
// Simulate a basic JSON-RPC message handler implementing the MSP server interface
class MyMutationServer implements MutationServer {
private requestIdCounter = 0;
// MSP method: discover mutants in a given scope
async discover(params: DiscoverParams): Promise<Mutant[]> {
console.log(`[Server] Received discover request for files: ${params.files?.map(f => f.fileName).join(', ')}`);
// In a real server, this would analyze code to find potential mutants
const mutants: Mutant[] = [
{
id: 'mutant-1',
mutatorName: 'BinaryExpression',
replacement: 'false', // Example replacement
location: { start: { line: 1, column: 10 }, end: { line: 1, column: 15 } },
fileName: 'src/example.js',
status: 'Pending'
},
];
return mutants;
}
// MSP method: run mutation tests for specific mutants
async mutationTest(params: MutationTestParams): Promise<MutationTestResult> {
console.log(`[Server] Received mutationTest request for mutants: ${params.mutants?.map(m => m.id).join(', ')}`);
// In a real server, this would execute tests against mutated code
const result: MutationTestResult = {
mutantResults: {
'mutant-1': { status: 'Killed', coveredBy: ['testA'] },
},
// ... other results like files, test results
};
return result;
}
// Example of how a client might construct and send a request
async simulateClientRequest() {
const server = new MyMutationServer();
const discoverRequest = {
jsonrpc: '2.0',
id: ++this.requestIdCounter,
method: 'discover',
params: {
files: [{ fileName: 'src/example.js' } as FileRange],
// Optional other params like 'testFramework'
} as DiscoverParams,
};
console.log('\n[Client] Sending discover request:', JSON.stringify(discoverRequest, null, 2));
const discoverResponse = await server.discover(discoverRequest.params);
console.log('[Client] Received discover response:', JSON.stringify(discoverResponse, null, 2));
const mutationTestRequest = {
jsonrpc: '2.0',
id: ++this.requestIdCounter,
method: 'mutationTest',
params: {
mutants: discoverResponse.slice(0, 1), // Test the first discovered mutant
// Optional other params
} as MutationTestParams,
};
console.log('\n[Client] Sending mutationTest request:', JSON.stringify(mutationTestRequest, null, 2));
const mutationTestResponse = await server.mutationTest(mutationTestRequest.params);
console.log('[Client] Received mutationTest response:', JSON.stringify(mutationTestResponse, null, 2));
}
}
const simulator = new MyMutationServer();
simulator.simulateClientRequest();