Skip to content

Fix non-compliant JSON-RPC error responses in servlet transports - #1314

Closed
sratz wants to merge 1 commit into
modelcontextprotocol:mainfrom
sratz:fix-mcperror-serialization
Closed

sratz wants to merge 1 commit into
modelcontextprotocol:mainfrom
sratz:fix-mcperror-serialization

Conversation

@sratz

@sratz sratz commented Nov 5, 2026

Copy link
Copy Markdown

The servlet transports returned error bodies that did not conform to the MCP JSON-RPC response format: they omitted the required "jsonrpc" field, used "jsonRpcError" instead of "error", and did not include the request ID even where it was available.

These malformed responses resulted from serializing McpError directly rather than constructing a JSONRPCResponse error envelope. Because McpError extends RuntimeException, this also caused information disclosure: Jackson included exception internals such as stack traces in the response.

Before (wrong; illustrative response with an abbreviated stack trace):

{
  "jsonRpcError": {
    "code": -123,
    "message": "foo"
  },
  "message": "foo",
  "localizedMessage": "foo",
  "cause": null,
  "suppressed": [],
  "stackTrace": [
    {
      "className": "io.modelcontextprotocol.spec.McpError$Builder",
      "methodName": "build",
      "fileName": "McpError.java",
      "lineNumber": 81,
      "nativeMethod": false
    }
  ]
}

After (correct; no request ID is available in this example):

{
  "jsonrpc": "2.0",
  "error": {
    "code": -123,
    "message": "foo"
  }
}

Write typed JSONRPCResponse envelopes across the SSE, streamable, and stateless servlet transports. Allow error responses without an ID while continuing to require IDs on successful responses, and propagate request IDs where already parsed. Preserve the public McpError response writer as a wrapper that unwraps the protocol error.

Add regression coverage for HTTP error envelopes, request IDs, and exclusion of exception properties, including causes and suppressed exceptions. Update tests that expected a top-level exception message.

Fixes #955

The servlet transports returned error bodies that did not conform to the
MCP JSON-RPC response format: they omitted the required "jsonrpc" field,
used "jsonRpcError" instead of "error", and did not include the request ID
even where it was available.

These malformed responses resulted from serializing McpError directly
rather than constructing a JSONRPCResponse error envelope. Because
McpError extends RuntimeException, this also caused information disclosure:
Jackson included exception internals such as stack traces in the response.

Before (wrong; illustrative response with an abbreviated stack trace):

```json
{
  "jsonRpcError": {
    "code": -123,
    "message": "foo"
  },
  "message": "foo",
  "localizedMessage": "foo",
  "cause": null,
  "suppressed": [],
  "stackTrace": [
    {
      "className": "io.modelcontextprotocol.spec.McpError$Builder",
      "methodName": "build",
      "fileName": "McpError.java",
      "lineNumber": 81,
      "nativeMethod": false
    }
  ]
}
```

After (correct; no request ID is available in this example):

```json
{
  "jsonrpc": "2.0",
  "error": {
    "code": -123,
    "message": "foo"
  }
}
```

Write typed JSONRPCResponse envelopes across the SSE, streamable, and
stateless servlet transports. Allow error responses without an ID while
continuing to require IDs on successful responses, and propagate request
IDs where already parsed. Preserve the public McpError response writer
as a wrapper that unwraps the protocol error.

Add regression coverage for HTTP error envelopes, request IDs, and
exclusion of exception properties, including causes and suppressed
exceptions. Update tests that expected a top-level exception message.
@github-actions github-actions Bot added the missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md) label Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

This PR has been closed automatically. This repo only keeps pull requests open when they come from a maintainer, or from a contributor a maintainer has assigned to the linked issue, and you aren't currently assigned to #955.

You're welcome to keep pushing commits here (just avoid force-pushing, since GitHub can't reopen a rewritten branch), but that on its own won't get the PR reviewed or the issue assigned. There's no need to open a new PR either way.

See CONTRIBUTING.md for the full policy.

Maintainers: reopen, remove missing-issue-link, or add bypass-issue-check to override.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Servlet transports serialize McpError exceptions instead of JSON-RPC error envelopes

1 participant