modelcontextprotocol / modelcontextprotocol/java-sdk

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

オープン
#961 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

enhancement needs confirmation P3
主要言語
Java
スター
3.7k
フォーク
1.1k
平均マージ
1日 15時間
マージ済み PR(30日)
9

説明

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.

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

HttpServletStreamableServerTransportProvider の Streamable HTTP POST ハンドラーから開始し、McpSchema.deserializeJsonRpcMessage が呼び出される前に Accept ヘッダーが確認されることを確認します。提供された curl コマンドを使って、ヘッダーを省略した場合、text/plain、application/x-www-form-urlencoded のケースを再現し、その後、無効または欠落しているリクエストのメディアタイプが JSON-RPC のディスパッチ前に拒否される一方で、application/json は引き続き機能することを確認します。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
java
領域
api, backend
issue の種類
バグ
難易度
3/5
見積もり時間
1〜2日
活発さ
静か
明瞭さ
おおむね明確
初心者へのやさしさ
65/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。