JSON-RPC 2.0 message format is correct
Why it matters
JSON-RPC 2.0 is the wire protocol underpinning every MCP interaction. A response missing "jsonrpc": "2.0" will be rejected by any compliant client. Responses that don't echo the request id leave the client unable to correlate responses to outstanding requests — causing hangs or misrouted results. Non-standard error formats ({'error': 'message'} instead of {'error': {'code': -32601, 'message': '...'}}) break client error handling. These are CWE-116 (improper encoding) failures that make the server non-interoperable.
Severity rationale
Critical because a malformed JSON-RPC envelope breaks the entire protocol layer — clients reject non-conformant responses and the server becomes non-functional regardless of tool correctness.
Remediation
Use the official MCP SDK transport, which handles JSON-RPC 2.0 formatting automatically. For custom implementations, every response must match this structure exactly.
// src/transport/handler.ts — JSON-RPC 2.0 response
const response = {
jsonrpc: '2.0', // required, must be the string '2.0'
id: request.id, // must match the incoming request id exactly
result: { /* ... */ }
}
// Error responses
const error = {
jsonrpc: '2.0',
id: request.id,
error: {
code: -32601, // integer, from standard codes
message: 'Method not found', // human-readable string
}
}
Never construct raw JSON and write it to stdout — use StdioServerTransport from the SDK.
Detection
-
ID:
jsonrpc-messages -
Severity:
critical -
What to look for: Count all message handlers. Enumerate which follow JSON-RPC 2.0 format (jsonrpc, method, id, params) vs. which deviate. If using an MCP SDK, the SDK handles JSON-RPC formatting. Verify the SDK is used correctly (not bypassed with custom message construction). For custom implementations, check that every outgoing message includes
"jsonrpc": "2.0". Check that responses include theidfrom the request. Check that error responses use the standarderrorobject withcodeandmessagefields. Check that notifications (noid) are handled without sending a response. -
Pass criteria: All outgoing messages follow JSON-RPC 2.0 format. Responses include the correct
id. Error responses use the standarderrorstructure. SDK-based servers use the SDK's transport correctly without bypassing it. 100% of messages must include the"jsonrpc": "2.0"field. -
Fail criteria: Custom message construction that omits
jsonrpcfield, responses without matchingid, non-standard error formats, or SDK transport bypassed with direct stdout writes. -
Skip (N/A) when: All checks skip when no MCP server is detected.
-
Cross-reference: For error response format, see
structured-errors. -
Detail on fail:
"Custom response handler writes JSON directly to stdout without 'jsonrpc': '2.0' field — responses will be rejected by clients"or"Error responses use custom format {'error': 'message'} instead of JSON-RPC {'error': {'code': -32600, 'message': '...'}}'" -
Remediation: Use the SDK's transport layer. If you must handle messages manually, follow JSON-RPC 2.0 exactly:
// JSON-RPC 2.0 compliant request format in src/transport/handler.ts { "jsonrpc": "2.0", "method": "tools/call", "id": 1, "params": { "name": "search", "arguments": {} } }// Correct — use the SDK transport const transport = new StdioServerTransport() await server.connect(transport) // If custom, every response must follow this format: const response = { jsonrpc: '2.0', id: request.id, // must match the request result: { /* ... */ } } // Errors must use this format: const errorResponse = { jsonrpc: '2.0', id: request.id, error: { code: -32601, // standard JSON-RPC error code message: 'Method not found', } }
External references
- cwe · CWE-116 — Improper Encoding or Escaping of Output
- external · jsonrpc-2.0-spec — JSON-RPC 2.0 Specification
- external · mcp-spec-transport — MCP Specification — Transport and JSON-RPC message format
Taxons
History
- 2026-04-18·v1.0.0·Initial import from mcp-server·automated