Streamable HTTP client: a 401 or 403 with a JSON-RPC error body and no WWW-Authenticate loses its HTTP status
Los mantenedores suelen responder en 3 días
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 68/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- rust
- Área
- authentication, backend
Línea de trabajo
Lee crates/rmcp/src/transport/common/reqwest/streamable_http_client.rs, especialmente la rama de respuesta no satisfactoria alrededor de las líneas 276–306 y las comprobaciones anteriores de 401/403. Compara su tratamiento con legacy_discover_response y la ruta de error de texto sin formato. El trabajo estará terminado cuando una respuesta 401 o 403 con un cuerpo de error JSON-RPC preserve el estado HTTP para quienes llaman; ejecuta las pruebas pertinentes del cliente HTTP Streamable.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
When a Streamable HTTP server answers a POST with 401 Unauthorized (or 403 Forbidden) without a WWW-Authenticate header and with a JSON-RPC error body, the reqwest client returns Ok(StreamableHttpPostResponse::Json(..)). The caller then gets an ordinary JSON-RPC error, ClientInitializeError::JsonRpcError during initialize or ServiceError::McpError after it, and cannot tell that the server rejected the credentials.
Where
The non-success branch parses the body with parse_json_rpc_error before it looks at the status:
AuthRequired and InsufficientScope come only from the checks above it, which require WWW-Authenticate. The same 401 with a plain-text body becomes UnexpectedServerResponse("HTTP 401 Unauthorized: …"), so the status survives on that path but not on the JSON one.
Reproduce
rmcp 3.5.1 (main has the same code). The server answers initialize with:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{"jsonrpc":"2.0","id":0,"error":{"code":-32001,"message":"Unauthorized"}}
serve_client over StreamableHttpClientTransport::with_client(reqwest::Client, config) fails with ClientInitializeError::JsonRpcError (code -32001, message "Unauthorized"). Nothing in the error says HTTP 401.
Why it matters
A client that sends a static token through custom_headers wants to tell the user that the token was rejected. An OAuth client wants to start or refresh authorization. Both need the status. The MCP authorization spec asks servers that implement authorization to send WWW-Authenticate with a 401, but a server that only takes a static token is not bound by that and may answer with a JSON-RPC error body instead.
Possible fixes
- Handle 401 and 403 before the JSON-RPC branch, as
legacy_discover_responsealready does ("Keep authentication failures and server errors on their original paths"). For example, returnAuthRequiredorInsufficientScopefor every 401 or 403, with the challenge optional, or returnUnexpectedServerResponse("HTTP 401 …")as the plain-text path does. - Or keep the JSON-RPC error and carry the HTTP status with it.
- Lenguaje dominante
- Rust
- Estrellas
- 4k
- Forks
- 662
- Merge medio
- 3 d 19 h
- PR fusionados (30 d)
- 35
Preparar el entorno
Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de modelcontextprotocol/rust-sdk
-
auth: refresh_token() requests offline_access that was never granted, so refreshes fail with invalid_scopePosiblemente ocupada @jstar0 la tomó hace 6 días. Abiertobug P1 ready for work T-security T-transport
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
modelcontextprotocol/rust-sdk#1330 ·
Los mantenedores suelen responder en 3 días
-
P3 question Stale T-documentation T-enhancement
Dificultad 1/5 Menos de una hora Aptitud para principiantes 86/100
modelcontextprotocol/rust-sdk#1155 · 1 comentario ·
Los mantenedores suelen responder en 3 días
-
Clean up sessions after legacy initialize parsing failsPosiblemente ocupada @bolinfest la tomó hace 1 día. Abiertobug P2 ready for work T-transport
modelcontextprotocol/rust-sdk#1357 · 1 comentario · 1 asignado ·
Los mantenedores suelen responder en 3 días
-
Expose body-free HTTP status and Retry-After metadataPosiblemente ocupada @bolinfest la tomó hace 1 día. Abierto
modelcontextprotocol/rust-sdk#1356 · 1 comentario · 1 asignado ·
Los mantenedores suelen responder en 3 días
-
DCR ignores advertised token endpoint authentication methodsPosiblemente ocupada @bolinfest la tomó hace 1 día. Abiertobug P2 ready for work T-security
modelcontextprotocol/rust-sdk#1355 · 1 comentario · 1 asignado ·
Los mantenedores suelen responder en 3 días
Todos los issues de modelcontextprotocol/rust-sdk
Issues similares
-
rustix::termios::Termios::OutputModes does not expost some const e.g. OLCUC for musl and macOSAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
bytecodealliance/rustix#1706 · 1 comentario ·
Los mantenedores suelen responder en 11 días
-
`LazyFrameRemote` and `ExecuteRemote` sinks do not accept `Path`, like `LazyFrame` sinks do.Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
-
Bump anybuf version to ^1.0.0Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
wormhole-foundation/wormhole#5062 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
Los mantenedores suelen responder en 1 día
-
RUSTSEC-2026-0348: Brotli certificate decompression can allocate up to 1 GiB before authenticationAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 60/100