# JSON-RPC 2.0 message format is correct

- **Pattern:** `ab-001833` (`mcp-server.transport-protocol.jsonrpc-messages`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001833
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001833)

## 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.

```ts
// 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 the `id` from the request. Check that error responses use the standard `error` object with `code` and `message` fields. Check that notifications (no `id`) 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 standard `error` structure. 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 `jsonrpc` field, responses without matching `id`, 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:

  ```ts
  // JSON-RPC 2.0 compliant request format in src/transport/handler.ts
  { "jsonrpc": "2.0", "method": "tools/call", "id": 1, "params": { "name": "search", "arguments": {} } }
  ```

  ```ts
  // 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
- external jsonrpc-2.0-spec — https://www.jsonrpc.org/specification
- external mcp-spec-transport — https://modelcontextprotocol.io/docs/concepts/transports

Taxons: inference-contract, error-resilience

HTML version: https://auditbuffet.com/patterns/ab-001833
