Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

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

Abierto Apto para principiantes
#3,670 2 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

@CRYPTONIKAV ya está trabajando en esto.

Desde el 10/10/2026.

  • #3675 de @CRYPTONIKAV — cerrado sin fusionar

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
75/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
python
Área
backend

Línea de trabajo

Empieza con _check_content_type en src/mcp/server/streamable_http.py, alrededor de la línea 534, donde el tipo de medio se compara distinguiendo mayúsculas y minúsculas, y compáralo con check_accept_headers en la línea 96 del mismo archivo. Reproduce el problema con el harness en proceso de tests/interaction/_connect.py usando un Content-Type con mayúsculas y minúsculas mezcladas. Está terminado cuando Application/JSON recibe una respuesta 200 y la nota de divergencia hosting:http:content-type-415 en tests/interaction/_requirements.py se actualiza para que coincida.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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.

Lenguaje dominante
Python
Estrellas
24.5k
Forks
4k
Merge medio
15 h 19 min
PR fusionados (30 d)
32

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de modelcontextprotocol/python-sdk

Todos los issues de modelcontextprotocol/python-sdk

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.