modelcontextprotocol / modelcontextprotocol/java-sdk

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

Open
#961 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement needs confirmation P3
Dominant language
Java
Stars
3.7k
Forks
1.1k
Avg merge
1d 15h
Merged PRs (30d)
9

Description

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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in HttpServletStreamableServerTransportProvider at the Streamable HTTP POST handler, where the Accept header is checked before McpSchema.deserializeJsonRpcMessage is called. Reproduce the omitted, text/plain, and application/x-www-form-urlencoded cases with the provided curl commands, then verify that invalid or missing request media types are rejected before JSON-RPC dispatch while application/json continues to work.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
api, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
65/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.