Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

Streamable HTTP server rejects a mixed-case Content-Type with 415

オープン 初心者向け
#3,670 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@CRYPTONIKAV がすでに取り組んでいます。

2026年10月10日 から。

  • #3675 @CRYPTONIKAV による — マージされずにクローズ

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
75/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
python
領域
backend

調査の方向性

src/mcp/server/streamable_http.py の約534行目にある _check_content_type から始めてください。ここではメディアタイプが大文字小文字を区別して比較されています。同じファイルの96行目にある check_accept_headers と比較してください。大文字と小文字が混在する Content-Type を使い、tests/interaction/_connect.py のインプロセスハーネスで再現してください。Application/JSON が200レスポンスを受け取り、tests/interaction/_requirements.py の hosting:http:content-type-415 の差異メモが一致するように更新されたら完了です。

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

説明

bug v1 v2
Release line

v2 (main, verified at 91941ed).

Description

StreamableHTTPServerTransport._check_content_type compares the request's Content-Type media type case-sensitively, so a POST with Content-Type: Application/JSON is answered 415 and the handshake never completes. Media types are case-insensitive per RFC 9110 §8.3.1.

Two things in the same request path already get this right, which is what makes the behaviour inconsistent rather than merely strict.

check_accept_headers (src/mcp/server/streamable_http.py:96) lowercases every Accept media type:

    accept_types = [media_type.strip().split(";")[0].strip().lower() for media_type in accept_header.split(",")]

and TransportSecurityMiddleware._validate_content_type (src/mcp/server/transport_security.py:96) lowercases too:

        return content_type is not None and content_type.lower().startswith("application/json")

The middleware runs first and is the lenient one, so a mixed-case header passes it and then hits the strict comparison in the transport (src/mcp/server/streamable_http.py:534):

        return any(part == CONTENT_TYPE_JSON for part in content_type_parts)

This also makes a note in your own conformance table wrong. tests/interaction/_requirements.py:3136 records a divergence for hosting:http:content-type-415 saying

The transport-security middleware rejects a non-JSON Content-Type with 400 'Invalid Content-Type header' before the request reaches the transport, so the transport's own 415 path is unreachable through any public entry point.

and the call site at line 569 carries # pragma: no cover on that belief. A mixed-case Content-Type is the public entry point that reaches it.

Example Code

Against the repo's own in-process harness on main at 91941ed, after uv sync --frozen:

import pytest
from mcp_types import CallToolRequestParams, CallToolResult, ListToolsResult, PaginatedRequestParams, TextContent

from mcp.server import Server, ServerRequestContext
from tests.interaction._connect import base_headers, initialize_body, mounted_app

pytestmark = pytest.mark.anyio


def _server() -> Server:
    async def list_tools(ctx: ServerRequestContext, params: PaginatedRequestParams | None) -> ListToolsResult:
        return ListToolsResult(tools=[])

    async def call_tool(ctx: ServerRequestContext, params: CallToolRequestParams) -> CallToolResult:
        return CallToolResult(content=[TextContent(text="done")])

    return Server("hosted", on_list_tools=list_tools, on_call_tool=call_tool)


async def test_content_type_casing() -> None:
    async with mounted_app(_server()) as (http, _):
        for value in ("application/json", "Application/JSON", "APPLICATION/JSON", "application/json; charset=utf-8"):
            r = await http.post("/mcp", json=initialize_body(), headers=base_headers() | {"content-type": value})
            print(f"{value!r:45} -> {r.status_code} {r.text[:60]!r}")

Output:

'application/json'                            -> 200 'event: message\r\ndata: {"jsonrpc":"2.0","id":1,"result":{"ca'
'Application/JSON'                            -> 415 '{"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":'
'APPLICATION/JSON'                            -> 415 '{"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":'
'application/json; charset=utf-8'             -> 200 'event: message\r\ndata: {"jsonrpc":"2.0","id":1,"result":{"ca'

The fix is to lowercase the parsed parts before comparing, the way the two siblings do.

Why it matters to me

Being straight about this, since you said that is what you use to prioritise: I do not have a client of my own that sends mixed-case, and I found this while checking the divergence notes in _requirements.py against the code. So the practical weight is not "my deployment is broken", it is that the server rejects a spec-conformant client at the handshake for a header-casing difference, and that your own conformance record currently states the opposite.

A one-line fix and a test are ready if you want a pull request. #2916 was the same fix against v1 and was closed in the backlog sweep, so I am filing the issue first as you asked there rather than opening another pull request unprompted.

Python & MCP Python SDK

Python 3.12 on macOS 27 arm64, repository main at 91941ed with uv sync --frozen.

主要言語
Python
スター
24.5k
フォーク
4k
平均マージ
15時間 19分
マージ済み PR(30日)
32

環境構築

はじめの一歩

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

modelcontextprotocol/python-sdk のほかの issue

modelcontextprotocol/python-sdk の issue をすべて見る

似ている issue

Python の issue をもっと見る

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

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