Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Streamable HTTP client: a 401 or 403 with a JSON-RPC error body and no WWW-Authenticate loses its HTTP status

Open Beginner friendly
#1,339 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 3 days

@aton-of-data is already working on this.

Since Oct 11, 2026.

  • #1341 by @aton-of-data — open

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
68/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
rust

Research direction

Read crates/rmcp/src/transport/common/reqwest/streamable_http_client.rs, especially the non-success response branch around lines 276–306 and the earlier 401/403 checks. Compare its handling with legacy_discover_response and the plain-text error path. Done when a 401 or 403 with a JSON-RPC error body preserves the HTTP status for callers; run the relevant Streamable HTTP client tests.

Written by the indexing model from the issue text.

Description

bug P2 ready for work T-security T-transport

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:

https://github.com/modelcontextprotocol/rust-sdk/blob/c1a8eea312613519b93f53b89a1dc74dc993f5b0/crates/rmcp/src/transport/common/reqwest/streamable_http_client.rs#L276-L306

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_response already does ("Keep authentication failures and server errors on their original paths"). For example, return AuthRequired or InsufficientScope for every 401 or 403, with the challenge optional, or return UnexpectedServerResponse("HTTP 401 …") as the plain-text path does.
  • Or keep the JSON-RPC error and carry the HTTP status with it.
Dominant language
Rust
Stars
4k
Forks
654
Avg merge
3d 4h
Merged PRs (30d)
37

Getting set up

Open in Codespaces

Starts the project's dev container in your browser, under your own GitHub account.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from modelcontextprotocol/rust-sdk

All issues in modelcontextprotocol/rust-sdk

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.