modelcontextprotocol / modelcontextprotocol/python-sdk

Mcp-Name accepts an orphan header, while Mcp-Param-* rejects it as a routing spoof

Abierto
#3,269 1 comentario 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

improves spec compliance P3 spec-2026-07-28 v2
Lenguaje dominante
Python
Estrellas
24.3k
Forks
4k
Merge medio
1 d 1 h
PR fusionados (30 d)
31

Descripción

What

validate_mcp_param_headers rejects an orphan Mcp-Param-* header, one present when the body argument is absent or null. classify_inbound_request accepts an orphan Mcp-Name header.

The Mcp-Param-* posture is deliberate and documented, in tests/shared/test_inbound.py:

"""SDK-defined posture on a spec gap: an orphan header is the routing-spoof case; go rejects too, ts skips."""

The Mcp-Name path has no equivalent branch. src/mcp/shared/inbound.py, line 447 on main:

body_value = cast("Mapping[str, Any]", body["params"]).get(name_key)
if body_value is not None and decode_header_value(headers.get(MCP_NAME_HEADER)) != body_value:

When body_value is None the whole check is skipped, including the case where a header claims a name.

Reproducer

from mcp.shared.inbound import (
    MCP_METHOD_HEADER, MCP_NAME_HEADER, MCP_PROTOCOL_VERSION_HEADER,
    InboundModernRoute, classify_inbound_request,
)

V = "2026-07-28"
body = {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {
    "arguments": {},
    "_meta": {"io.modelcontextprotocol/protocolVersion": V,
              "io.modelcontextprotocol/clientCapabilities": {}}}}

result = classify_inbound_request(body, headers={
    MCP_PROTOCOL_VERSION_HEADER: V,
    MCP_METHOD_HEADER: "tools/call",
    MCP_NAME_HEADER: "ping",          # claims a tool the body never names
})
assert isinstance(result, InboundModernRoute)   # passes today

Same shape for resources/read with uri.

Why this looks like an inconsistency rather than a decision

The existing Mcp-Name test covers a different case:

def test_header_rung_does_not_require_name_header_when_body_omits_the_named_param() -> None:
    """SDK-defined: ... the param's absence is INVALID_PARAMS later, not HEADER_MISMATCH here."""

matching_headers omits Mcp-Name when the body lacks the param, so this pins "body omits the param and no header is sent". That rationale reads correctly for that case: nobody asserted a name, so the defect is the missing param and INVALID_PARAMS is the right answer.

It does not obviously extend to a client that did assert one. There the header and body disagree, which is the condition the routing-spoof posture exists for, and Mcp-Name is the header an intermediary is most likely to route on, since it names the tool or resource rather than a secondary parameter.

To be clear about impact: the malformed request fails INVALID_PARAMS downstream either way, so nothing executes. The concrete cost is an intermediary routing or rate-limiting on a name the body never contained.

Ask

Is the Mcp-Param-* posture intended to apply to Mcp-Name? If so the fix mirrors the sibling branch, rejecting when body_value is None and the header is present, with a test alongside test_validate_mcp_param_headers_rejects_orphan_header_for_absent_or_null_argument.

If the asymmetry is intentional, a note in that test's docstring saying so would prevent the next reader drawing the same conclusion I did.

Happy to open the PR either way once you say which you would prefer.


AI disclosure per AI_POLICY: I directed this investigation and reviewed the result. The research and drafting were done by Claude Code, which found this while probing SEP-2243 handling with adversarial tests, having implemented the same validation in a TypeScript resource server. 87 probes against encode_header_value and decode_header_value found no defects there, including non-canonical base64, invalid UTF-8 and sentinel-literal collisions, so this is the only finding. Verified against main rather than the released 2.0.0 wheel.

Guía de contribución

Abrir la guía de contribución

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.

Línea de trabajo

Empieza en src/mcp/shared/inbound.py, en la rama classify_inbound_request alrededor de la línea 447, y después lee tests/shared/test_inbound.py, especialmente test_validate_mcp_param_headers_rejects_orphan_header_for_absent_or_null_argument y el test existente de Mcp-Name. Confirma con los maintainers la postura prevista; se considera terminado si hay una prueba de regresión y la validación correspondiente, o un docstring de prueba que explique la asimetría intencionada.

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

Evaluación

Stack tecnológico
python
Área
api, security
Tipo de issue
Error
Dificultad
3/5
Tiempo estimado
1-2 días
Estado de actividad
Tranquilo
Claridad
Bastante claro
Aptitud para principiantes
42/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.