Skip to content

Streamable HTTP server accepts POST requests with non-JSON request Content-Type #961

@cclabadmin

Description

@cclabadmin

Bug description

The Java SDK Streamable HTTP server accepts POST requests whose body is a valid JSON-RPC message even when the request Content-Type is missing or set to a non-JSON media type such as text/plain or application/x-www-form-urlencoded. These requests are processed normally and return HTTP 200.

I do not think the spec currently makes this requirement explicit. The MCP Streamable HTTP spec for 2025-11-25 requires POST request bodies to be JSON-RPC messages and requires the client Accept header to include both application/json and text/event-stream, but it does not explicitly say that servers must return HTTP 415 for a missing or non-JSON request Content-Type. I am filing this as an interoperability and hardening issue: if the body is JSON-RPC, the Java SDK currently accepts it regardless of the declared request media type.

In HttpServletStreamableServerTransportProvider, the POST handler validates the Accept header, then calls McpSchema.deserializeJsonRpcMessage(jsonMapper, body.toString()). I did not find a request Content-Type check before dispatch. This matches the observed behavior.

Environment

  • Repository: modelcontextprotocol/java-sdk
  • stable version: v1.1.2 (e9e1a2f34dedb72008d90e9919052d46eb2b701c, pinned 2026-05-03)
  • main snapshot: from 2026-05-15 (c09ee67f60260bd258b1a1aab9315a647a239d86, v0.6.0-355-gc09ee67f)
  • Transport: Streamable HTTP server, stateful mode
  • Java runtime used for repro: OpenJDK 21.0.10
  • SDK build target: Java 17 (java.version, maven.compiler.source, and maven.compiler.target are set to 17 in the SDK pom.xml)

Steps to reproduce

  1. Start a Java SDK Streamable HTTP server.
  2. Complete a normal initialize request and send notifications/initialized with the returned Mcp-Session-Id.
  3. Send a JSON-RPC request body with Content-Type: text/plain.
  4. Repeat with Content-Type: application/x-www-form-urlencoded.
  5. Repeat with the Content-Type header omitted.
  6. Observe that all three non-JSON or missing-content-type requests are dispatched and return HTTP 200.

In my retained logs, both stable and current behaved the same way:

Content-Type omitted: HTTP 200, request dispatched
Content-Type text/plain: HTTP 200, request dispatched
Content-Type application/x-www-form-urlencoded: HTTP 200, request dispatched
Content-Type application/json; charset=utf-8: HTTP 200, request dispatched

Expected behavior

I would expect the Streamable HTTP server to reject POST requests whose request Content-Type is missing or not a JSON media type before dispatching the JSON-RPC message, for example with HTTP 415 Unsupported Media Type or another clear 4xx transport error.

If accepting JSON bodies under non-JSON media types is intentional, it would be helpful to document that behavior explicitly, because other SDK server implementations reject the same probes and because accepting text/plain can matter for browser/CORS hardening.

Minimal Complete Reproducible example

Set ENDPOINT to a Java SDK Streamable HTTP endpoint:

ENDPOINT=http://127.0.0.1:8080/mcp

Initialize and copy the returned Mcp-Session-Id header into SID:

curl -i -sS --http1.1 -X POST "$ENDPOINT" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2025-11-25' \
  --data '{"jsonrpc":"2.0","id":"init-1","method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"content-type-repro","version":"0.1.0"}}}'

Send the initialized notification:

curl -i -sS --http1.1 -X POST "$ENDPOINT" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2025-11-25' \
  -H "Mcp-Session-Id: $SID" \
  --data '{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}'

Send a normal JSON-RPC request, but declare it as text/plain:

curl -i -sS --http1.1 -X POST "$ENDPOINT" \
  -H 'Content-Type: text/plain' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2025-11-25' \
  -H "Mcp-Session-Id: $SID" \
  --data '{"jsonrpc":"2.0","id":"ct-1","method":"tools/list","params":{}}'

Observed result:

HTTP/1.1 200 OK
Content-Type: text/event-stream;charset=UTF-8

id: ...
event: message
data: {"jsonrpc":"2.0","id":"ct-1","result":{"tools":[...]}}

The same behavior is reproducible with Content-Type: application/x-www-form-urlencoded and with the Content-Type header omitted.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions